Preliminaries
Load libraries, functions and data.
#ensloc_wave01 <- '/scratch/hadaw/jules_postprocess/u-ck006/'
ensloc_wave01 <- '/data/users/hadaw/JULES_ES_PPE/u-ck006/'
# Number of ensemble members (out of 500) to use for training in wave01
ntrain_wave01 <- 400
makeJulesEnsembleModernValue <- function(ensloc, varlist, nstart, nend, ix = 144:164){
nens <- (nend - nstart) + 1
datmat <- matrix(nrow = nens, ncol = length(varlist))
colnames(datmat) <- varlist
enslist <- paste("P", formatC(nstart:nend, width=4, flag="0"), sep="")
for(i in 1:nens){
vec <- rep(NA, length(varlist))
ensmember <- enslist[i]
fn <- paste0(ensloc,'JULES-ES-1p0_',ensmember,'_Annual_global.nc')
try(nc <- nc_open(paste0(fn)))
try(vec <- sapply(varlist, FUN = modernValue, nc = nc, ix = ix))
datmat[i, ] <- vec
nc_close(nc)
}
return(list(datmat = datmat, enslist = enslist))
}
nstart <- 499
nend <- (nstart + ntrain_wave01) - 1
if (file.exists("ensemble_wave01_2022-04-08.rdata")) {
load("ensemble_wave01_2022-04-08.rdata")
} else {
ens_wave01_mv <- makeJulesEnsembleModernValue(ensloc = ensloc_wave01,
varlist = y_names_sum,
nstart = nstart,
nend = nend,
ix = 144:164)
save(ens_wave01_mv, file ="ensemble_wave01_2022-04-08.rdata")
}
lhs_wave01 <- read.table( '../conf_files_augment_JULES-ES-1p0/lhs_example.txt', header = TRUE)
X_wave01 = normalize(lhs_wave01, wrt = rbind(lhs_i, lhs_ii, lhs_wave01))
colnames(X_wave01) = colnames(lhs_wave01)
# Match the 400 outputs we're using in the training data
X_wave01_train <- X_wave01[1:ntrain_wave01, ]
# Modern values that we use for constraints
Y_const_wave01 <- ens_wave01_mv$datmat[, ynames_const]
Y_const_wave01_scaled <- sweep(Y_const_wave01, 2, STATS = scalevec, FUN = '/' )
How many run failures were there?
There are no NAs but some relative humidity values are infinite. There are no “low NPP” ensemble members
low_npp_ix_wave01 <- which(ens_wave01_mv$datmat[,'npp_nlim_lnd_sum'] < 1e5)
min(ens_wave01_mv$datmat[,'npp_nlim_lnd_sum'])
[1] 117464.6
Y_wave01_nlevel0_ix <- which(is.na(ens_wave01_mv$datmat[,'nbp_lnd_sum']))
all(is.finite(ens_wave01_mv$datmat))
[1] FALSE
which(!is.finite(ens_wave01_mv$datmat), arr.ind = TRUE)
row col
[1,] 140 9
[2,] 232 9
[3,] 249 9
[4,] 300 9
ens_wave01_mv$datmat[which(!is.finite(ens_wave01_mv$datmat), arr.ind = TRUE)]
[1] Inf Inf Inf Inf
colnames(ens_wave01_mv$datmat)[9]
[1] "rh_lnd_sum"
Wave00 level1a Ensemble behaviour in all outputs
Need units
#pdf(file = 'figs/wave00-level1a-modern-value-hists.pdf', width = 8, height = 10)
par(mfrow = c(5,3), fg = 'darkgrey', las = 1)
for (i in y_names_select){
hist(Y_level1a[, i], main = i, col = 'grey', xlab = select_units[i])
abline(v = standard_modern_value[i], col = 'black')
}

#dev.off()
Wave00/Wave01 Ensemble behaviour in key (constraining) outputs.
Global mean for the 20 years at the end of the 20th Century. There is still a significant low bias on cVeg output.
wave00col <- 'skyblue2'
wave01col <- 'tomato2'
wave00col <- 'dodgerblue2'
wave01col <- 'firebrick'
rangecol <- 'grey'
# Histogram of level 1 constraints
hcol = 'darkgrey'
lcol = 'black'
#pdf(file = 'figs/level_2_constraints_hists.pdf', width = 6, height = 5)
par(mfrow = c(2,2), fg = 'darkgrey', las = 1, oma = c(0.1, 0.1, 4, 0.1))
trunc <- function(x, vec){
dat <- x[x < max(vec) & x > min(vec) ]
dat
}
h <- hist(Y_const_level1a_scaled[,'nbp_lnd_sum'], main = 'NBP', xlab = 'GtC/year', col = makeTransparent(wave00col,150))
hist(trunc(Y_const_wave01_scaled [,'nbp_lnd_sum'], h$breaks) ,
col = makeTransparent(wave01col,150) , breaks = h$breaks, add = TRUE)
rug(Y_const_stan_scaled['nbp_lnd_sum'], lwd = 2)
polygon(x = c(0, 100, 100, 0), y = c(0, 0, 1000, 1000),
col = makeTransparent(rangecol, 60),
border = makeTransparent(rangecol))
h <- hist(Y_const_level1a_scaled[,'npp_nlim_lnd_sum'],col = makeTransparent(wave00col,150), main = 'NPP', xlab = 'GtC/year')
hist(trunc(Y_const_wave01_scaled [,'npp_nlim_lnd_sum'], h$breaks) ,
col = makeTransparent(wave01col) , breaks = h$breaks, add = TRUE)
rug(Y_const_stan_scaled['npp_nlim_lnd_sum'], lwd = 2)
polygon(x = c(35, 80, 80, 35), y = c(0, 0, 1000, 1000),
col = makeTransparent(rangecol, 60),
border = makeTransparent(rangecol))
h <- hist(Y_const_level1a_scaled[,'cSoil_lnd_sum'], col = makeTransparent(wave00col,150), main = 'Soil Carbon', xlab = 'GtC')
hist(trunc(Y_const_wave01_scaled [,'cSoil_lnd_sum'], h$breaks) ,
col = makeTransparent(wave01col,150) , breaks = h$breaks, add = TRUE)
rug(Y_const_stan_scaled['cSoil_lnd_sum'], lwd = 2)
polygon(x = c(750, 3000, 3000, 750), y = c(0, 0, 1000, 1000),
col = makeTransparent(rangecol, 60),
border = makeTransparent(rangecol))
h <- hist(Y_const_level1a_scaled[,'cVeg_lnd_sum'], col = makeTransparent(wave00col,150), main = 'Vegetation Carbon', xlab = 'GtC')
hist(trunc(Y_const_wave01_scaled [,'cVeg_lnd_sum'], h$breaks) ,
col = makeTransparent(wave01col,150) , breaks = h$breaks, add = TRUE)
rug(Y_const_stan_scaled['cVeg_lnd_sum'], lwd = 2)
polygon(x = c(300, 800, 800, 300), y = c(0, 0, 1000, 1000),
col = makeTransparent(rangecol, 60),
border = makeTransparent(rangecol))
#dev.off()
reset()
legend('top', horiz = TRUE, fill = c(makeTransparent(wave00col, 150), makeTransparent(wave01col, 150), makeTransparent(rangecol, 60)), legend = c('Wave00', 'Wave01', 'AW range'))

What proportion of models now fall within Andy’s constraints?
Just over a third! Better than before, but still not great. Pointing at a significant model discrepency in cVeg
AW_constraints <- matrix(nrow = 2, ncol = length(ynames_const))
AW_constraints[1,] <- c(0, 35, 750, 300)
AW_constraints[2,] <- c(100, 80, 3000, 800)
colnames(AW_constraints) <- ynames_const
rownames(AW_constraints) <- c('min', 'max')
# conform to Andy's basic constraints
#level2_ix_wave01 <- which(apply(Y_const_wave01_scaled, 1, FUN = withinRange, maxes = AW_constraints[2,], mins = AW_constraints[1,] ))
#nlevel2_ix_wave01 <- which(apply(Y_const_wave01_scaled, 1, FUN = withinRange, maxes = AW_constraints[2,], mins = AW_constraints[1,] ) == FALSE)
level2_ix_wave01 <- which(Y_const_wave01_scaled[,'nbp_lnd_sum'] > 0 &
Y_const_wave01_scaled[,'npp_nlim_lnd_sum'] > 35 & Y_const_wave01_scaled[,'npp_nlim_lnd_sum'] < 80 &
Y_const_wave01_scaled[,'cSoil_lnd_sum'] > 750 & Y_const_wave01_scaled[,'cSoil_lnd_sum'] < 3000 &
Y_const_wave01_scaled[,'cVeg_lnd_sum'] > 300 & Y_const_wave01_scaled[,'cVeg_lnd_sum'] < 800
)
Of the 400 members of the wave01 ensemble, 128 pass Andy Wiltshire’s Level 2 constraints.
length(level2_ix_wave01)
[1] 128
Pairs plot of the inputs that pass the constraints with respect to the limits of the original ensemble.
pairs(X_wave01[level2_ix_wave01, ],
xlim = c(0,1),
ylim = c(0,1),
gap = 0,
lower.panel = NULL,
pch = 20,
col = makeTransparent(wave01col,200)
)

Timeseries of mean carbon cycle properties over whole run.
#
#This has a problem with variable lengths of time in the runs (eg P0509)
# makeTimeseriesEnsemble <- function(ensloc, variable, nstart, nend, nts = 164, cn = 1850:2013){
#
# ysec <- 31536000
#
# nens <- (nend - nstart) + 1
# # nens is number of ensemble members
# # nts length of timeseries
# # cn is colnames()
# datmat <- matrix(NA, nrow = nens, ncol = nts)
# colnames(datmat) <- cn
#
# enslist <- paste("P", formatC(nstart:nend, width=4, flag="0"), sep="")
#
# for(i in 1:nens){
#
# #vec <- rep(NA,nts)
#
# ensmember <- enslist[i]
# #print(ensmember)
#
# fn <- paste0(ensloc,'JULES-ES-1p0_',ensmember,'_Annual_global.nc')
#
#
# try(nc <- nc_open(paste0(fn)))
# try(localtime <- ncvar_get(nc, 'time'))
#
# # This part compensates for the fact that sometimes years are missing
# try(localyear <- floor(2010 + (localtime / ysec)))
# try(ix <- which(cn%in%localyear))
#
# try(dat <- extractTimeseries(nc, variable))
#
# try(datmat[i, ix] <- dat)
# nc_close(nc)
# }
# datmat
# }
# makeTimeseriesEnsemble <- function(ensloc, variable, nstart, nend, nts = 164, cn = 1850:2013){
#
# nens <- (nend - nstart) + 1
# # nens is number of ensemble members
# # nts length of timeseries
# # cn is colnames()
# datmat <- matrix(NA, nrow = nens, ncol = nts)
# colnames(datmat) <- cn
#
# enslist <- paste("P", formatC(nstart:nend, width=4, flag="0"), sep="")
# #floc <- paste0(ensloc,ensmember,subdir)
#
# for(i in 1:nens){
#
# vec <- rep(NA,nts)
#
# ensmember <- enslist[i]
#
# fn <- paste0(ensloc,ensmember,'/stats/','JULES-ES-1p0_',ensmember,'_Annual_global.nc')
#
#
# try(nc <- nc_open(paste0(fn)))
# try(dat <- extractTimeseries(nc, variable))
#
# datmat[i, ] <- dat
# nc_close(nc)
# }
# datmat
# }
if (file.exists("ensemble_timeseries_wave01_2022-04-08.rdata")) {
load("ensemble_timeseries_wave01_2022-04-08.rdata")
} else {
# primary carbon cycle outputs
npp_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend, variable = "npp_nlim_lnd_sum") / (1e12/ysec)
nbp_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend,variable = "nbp_lnd_sum") / (1e12/ysec)
cSoil_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend,variable = "cSoil_lnd_sum") / 1e12
cVeg_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend,variable = "cVeg_lnd_sum") / 1e12
#
#
lai_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend,variable = "lai_lnd_mean")
#
# # fluxes
rh_lnd_sum_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend, variable = "rh_lnd_sum") / (1e12/ysec)
fLuc_lnd_sum_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend, variable = "fLuc_lnd_sum") / (1e12/ysec)
fHarvest_lnd_sum_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend, variable = "fHarvest_lnd_sum") / (1e12/ysec)
#
#
# # fractions
treeFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend, variable = "treeFrac_lnd_mean")
shrubFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend, variable = "shrubFrac_lnd_mean")
baresoilFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01,nstart = nstart, nend = nend, variable = "baresoilFrac_lnd_mean")
c3PftFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01, nstart = nstart, nend = nend,variable = "c3PftFrac_lnd_mean")
c4PftFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc_wave01, nstart = nstart, nend = nend,variable = "c4PftFrac_lnd_mean")
save(npp_ens_wave01,
nbp_ens_wave01,
cSoil_ens_wave01,
cVeg_ens_wave01,
lai_lnd_mean_ens_wave01,
rh_lnd_sum_ens_wave01,
fLuc_lnd_sum_ens_wave01,
fHarvest_lnd_sum_ens_wave01,
treeFrac_lnd_mean_ens_wave01,
shrubFrac_lnd_mean_ens_wave01,
baresoilFrac_lnd_mean_ens_wave01,
c3PftFrac_lnd_mean_ens_wave01,
c4PftFrac_lnd_mean_ens_wave01,
file = "ensemble_timeseries_wave01_2022-04-08.rdata" )
}
#total_land_carbon_ens_wave01 <- cSoil_ens_wave01 + cVeg_ens_wave01
timeseries outliers
Timeseries that have problems. NBP, RH and cSoil seems to have large outliers
# These indices reference the separate ensembles
# cSoil over 6000
# rh over 200
# nbp less than -15
cSoil_outlier_ix_wave00 <- unique(which(cSoil_ens > 6000, arr.ind = TRUE)[,'row'])
cSoil_outlier_ix_wave01 <- unique(which(cSoil_ens_wave01 > 6000, arr.ind = TRUE)[,'row'])
nbp_outlier_ix_wave00 <- unique(which(nbp_ens < -15, arr.ind = TRUE)[,'row'])
nbp_outlier_ix_wave01 <- unique(which(nbp_ens_wave01 < -15, arr.ind = TRUE)[,'row'])
rh_lnd_sum_outlier_ix_wave00 <- unique(which(rh_lnd_sum_ens > 200, arr.ind = TRUE)[,'row'])
rh_lnd_sum_outlier_ix_wave01 <- unique(which(rh_lnd_sum_ens_wave01 > 200, arr.ind = TRUE)[,'row'])
# are there additional excluded indices to those already excluded by the constraint
wave01_all_ix <- 1:ntrain_wave01
# Indices excluded in wave01 level2
level2_nix_wave01 <- setdiff(wave01_all_ix, level2_ix_wave01)
# would be interesting to see if these look normal in other ways
ts_outliers_ix_wave01 <- unique(c(cSoil_outlier_ix_wave01,nbp_outlier_ix_wave01, rh_lnd_sum_outlier_ix_wave01))
# are there any that are not excluded by level 2? (I assume so)
intersect(ts_outliers_ix_wave01, level2_nix_wave01)
[1] 78 31 243 249 300
without_outliers_ix_wave01 <- setdiff(wave01_all_ix,ts_outliers_ix_wave01)
# Remove these from the wave01 ensemble to remove outliers and excluded ensemble members
level2_and_ts_outliers_nix_wave01 <- union(level2_nix_wave01, ts_outliers_ix_wave01)
level2a_ix_wave01 <- setdiff(wave01_all_ix, level2_and_ts_outliers_nix_wave01)
wave00_all_ix <- 1:499
ts_outliers_ix_wave00 <- unique(c(cSoil_outlier_ix_wave00,nbp_outlier_ix_wave00, rh_lnd_sum_outlier_ix_wave00))
without_outliers_ix_wave00 <- setdiff(wave00_all_ix,ts_outliers_ix_wave00)
lcol_wave0 <- makeTransparent('dodgerblue2', 120)
lcol_wave01 <- makeTransparent('firebrick', 120)
lcol_wave01_level2 <- 'gold'
stancol = 'black'
linePlotMultiEns <- function(years, ens1, ens2, ens3, col1, col2, col3, ylab, main, ylim = NULL){
# Plot wave00 and wave01 timeseries on top of one another
nt <- length(years)
if(is.null(ylim)){
ylim = range(c(ens1[,1], ens1[,nt], ens2[,1], ens2[ ,nt], ens3[,1], ens3[, nt]))
}
else ylim <- ylim
matplot(years, t(ens1), type = 'l', lty = 'solid',ylim = ylim, col = col1,
ylab = ylab, main = main, xlab = '',
bty = 'n')
matlines(years, t(ens2), col = col2, lty = 'solid')
matlines(years, t(ens3), col = col3, lty = 'solid')
}
#pdf(file = 'figs/carbon-cycle-timeseries-waves-constrained.pdf', width = 10, height = 12)
par(mfrow= c(3,5), las = 1, mar = c(4,4,1,0))
linePlotMultiEns(years = years, ens1 = npp_ens[without_outliers_ix_wave00,],
ens2 = npp_ens_wave01[without_outliers_ix_wave01,],
ens3 = npp_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'NPP')
lines(years,npp_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = nbp_ens[without_outliers_ix_wave00,],
ens2 = nbp_ens_wave01[without_outliers_ix_wave01,],
ens3 = nbp_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01,col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'NBP', ylim = c(-10,10))
lines(years, nbp_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = cSoil_ens[without_outliers_ix_wave00,],
ens2 = cSoil_ens_wave01[without_outliers_ix_wave01,],
ens3 = cSoil_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'cSoil', ylim = range(c(cSoil_ens[,1], cSoil_ens[,164])))
lines(years, cSoil_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = cVeg_ens[without_outliers_ix_wave00,],
ens2 = cVeg_ens_wave01[without_outliers_ix_wave01,],
ens3 = cVeg_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'cVeg')
lines(years, cVeg_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = lai_lnd_mean_ens[without_outliers_ix_wave00,],
ens2 = lai_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
ens3 = lai_lnd_mean_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'Lai')
lines(years, lai_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = rh_lnd_sum_ens[without_outliers_ix_wave00,],
ens2 = rh_lnd_sum_ens_wave01[without_outliers_ix_wave01,],
ens3 = rh_lnd_sum_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'RH')
lines(years, rh_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = fLuc_lnd_sum_ens[without_outliers_ix_wave00,],
ens2 = fLuc_lnd_sum_ens_wave01[without_outliers_ix_wave01,],
ens3 = fLuc_lnd_sum_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'fLuc')
lines(years, fLuc_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = fHarvest_lnd_sum_ens[without_outliers_ix_wave00,],
ens2 = fHarvest_lnd_sum_ens_wave01[without_outliers_ix_wave01,],
ens3 = fHarvest_lnd_sum_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'fHarvest')
lines(years, fHarvest_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = treeFrac_lnd_mean_ens[without_outliers_ix_wave00,],
ens2 = treeFrac_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
ens3 = treeFrac_lnd_mean_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = '%', main = 'treefrac'
)
lines(years, treeFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = shrubFrac_lnd_mean_ens[without_outliers_ix_wave00,],
ens2 = shrubFrac_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
ens3 = shrubFrac_lnd_mean_ens[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = '%', main = 'shrubfrac'
)
lines(years, shrubFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = baresoilFrac_lnd_mean_ens[without_outliers_ix_wave00,],
ens2 = baresoilFrac_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
ens3 = baresoilFrac_lnd_mean_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = '%', main = 'baresoilfrac')
lines(years, baresoilFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, c3PftFrac_lnd_mean_ens[without_outliers_ix_wave00,],
ens2 = c3PftFrac_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
ens3 = c3PftFrac_lnd_mean_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = '%', main = 'c3PftFrac')
lines(years, c3PftFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, c4PftFrac_lnd_mean_ens[without_outliers_ix_wave00,],
ens2 = c4PftFrac_lnd_mean_ens_wave01[without_outliers_ix_wave01,],
ens3 = c4PftFrac_lnd_mean_ens_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = '%', main = 'c3PftFrac')
lines(years, c4PftFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)
reset()
legend('bottomright', legend = c('wave00','wave01','wave01 level2','standard'), lty = 'solid', lwd = 1.5, col = c(lcol_wave0, lcol_wave01, lcol_wave01_level2, stancol), inset = c(0.05, 0.15) )

#dev.off()
This is a plot of timeseries of Wave00, Wave01, and level2-constrained wave01 on top of one another. We see that the wave01 is closer to the standard than wave00, and the level-2 constrained wave01 ensemble is often closer again. However, there are still quite large discrepancies. For example, baresoilfrac is often way too high, shrubfrac is often too low (though both these span the standard). Treefrac is away from zero, but still often too low or too high. While fHarvest looks good, fLuc does not appear constrained by the process at all. RH (soil respiration) looks well constrained, whereas lai is often too low.
One thing we could do next is constrain input space again, using observations or “tolerance to error” on some or all of these outputs.
We could also extend sensitivity analysis to work out what controls e.g. treefrac.
A summary of what gets constrained when you constrain the top-level variables would be a useful aanalysis.
Anomaly timeseries
Similar to above, but output anomaly
npp_ens_anom_wave01 <- anomalizeTSmatrix(npp_ens_wave01, 1:20)
nbp_ens_anom_wave01 <- anomalizeTSmatrix(nbp_ens_wave01, 1:20)
cSoil_ens_anom_wave01 <- anomalizeTSmatrix(cSoil_ens_wave01, 1:20)
cVeg_ens_anom_wave01 <- anomalizeTSmatrix(cVeg_ens_wave01, 1:20)
rh_lnd_sum_ens_anom_wave01 <- anomalizeTSmatrix(rh_lnd_sum_ens_wave01, 1:20)
fLuc_lnd_sum_ens_anom_wave01 <- anomalizeTSmatrix(fLuc_lnd_sum_ens_wave01, 1:20)
lai_lnd_mean_ens_anom_wave01 <- anomalizeTSmatrix(lai_lnd_mean_ens_wave01, 1:20)
fHarvest_lnd_sum_ens_anom_wave01 <- anomalizeTSmatrix(fHarvest_lnd_sum_ens_wave01, 1:20)
treeFrac_lnd_mean_ens_anom_wave01 <- anomalizeTSmatrix(treeFrac_lnd_mean_ens_wave01, 1:20)
shrubFrac_lnd_mean_ens_anom_wave01 <- anomalizeTSmatrix(shrubFrac_lnd_mean_ens_wave01, 1:20)
baresoilFrac_lnd_mean_ens_anom_wave01 <- anomalizeTSmatrix(baresoilFrac_lnd_mean_ens_wave01, 1:20)
c3PftFrac_lnd_mean_ens_anom_wave01 <- anomalizeTSmatrix(c3PftFrac_lnd_mean_ens_wave01, 1:20)
c4PftFrac_lnd_mean_ens_anom_wave01 <- anomalizeTSmatrix(c4PftFrac_lnd_mean_ens_wave01, 1:20)
#total_land_carbon_anom_wave01 <- anomalizeTSmatrix(total_land_carbon_ens_wave01, 1:20)
anomalizeTS <- function(x, ix = 1:20){x - mean(x[ix]) }
npp_stan_anom <- anomalizeTS(npp_stan)
nbp_stan_anom <- anomalizeTS(nbp_stan)
cSoil_stan_anom <-anomalizeTS(cSoil_stan)
cVeg_stan_anom <- anomalizeTS(cVeg_stan)
lai_lnd_mean_stan_anom <- anomalizeTS(lai_lnd_mean_stan)
# fluxes
rh_lnd_sum_stan_anom <- anomalizeTS(rh_lnd_sum_stan)
fLuc_lnd_sum_stan_anom <- anomalizeTS(fLuc_lnd_sum_stan)
fHarvest_lnd_sum_stan_anom <- anomalizeTS(fHarvest_lnd_sum_stan)
# fractions
treeFrac_lnd_mean_stan_anom <- anomalizeTS(treeFrac_lnd_mean_stan)
shrubFrac_lnd_mean_stan_anom <- anomalizeTS(shrubFrac_lnd_mean_stan)
baresoilFrac_lnd_mean_stan_anom <- anomalizeTS(baresoilFrac_lnd_mean_stan)
c3PftFrac_lnd_mean_stan_anom <- anomalizeTS(c3PftFrac_lnd_mean_stan)
c4PftFrac_lnd_mean_stan_anom <- anomalizeTS(c4PftFrac_lnd_mean_stan)
#pdf(file = 'figs/carbon-cycle-timeseries-anomaly-waves-constrained.pdf', width = 10, height = 12)
par(mfrow= c(3,5), las = 1, mar = c(4,4,1,0))
linePlotMultiEns(years = years, ens1 = npp_ens_anom[without_outliers_ix_wave00,],
ens2 = npp_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = npp_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'NPP')
lines(years,npp_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = nbp_ens_anom[without_outliers_ix_wave00,],
ens2 = nbp_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = nbp_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01,col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'NBP', ylim = c(-10,10))
lines(years, nbp_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = cSoil_ens_anom[without_outliers_ix_wave00,],
ens2 = cSoil_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = cSoil_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'cSoil', ylim = range(c(cSoil_ens_anom[,1], cSoil_ens_anom[,164])))
lines(years, cSoil_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = cVeg_ens_anom[without_outliers_ix_wave00,],
ens2 = cVeg_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = cVeg_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'cVeg')
lines(years, cVeg_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = lai_lnd_mean_ens_anom[without_outliers_ix_wave00,],
ens2 = lai_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = lai_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'Lai')
lines(years, lai_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = rh_lnd_sum_ens_anom[without_outliers_ix_wave00,],
ens2 = rh_lnd_sum_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = rh_lnd_sum_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'RH')
lines(years, rh_lnd_sum_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = fLuc_lnd_sum_ens_anom[without_outliers_ix_wave00,],
ens2 = fLuc_lnd_sum_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = fLuc_lnd_sum_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'fLuc')
lines(years, fLuc_lnd_sum_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = fHarvest_lnd_sum_ens_anom[without_outliers_ix_wave00,],
ens2 = fHarvest_lnd_sum_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = fHarvest_lnd_sum_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = 'GtC', main = 'fHarvest')
lines(years, fHarvest_lnd_sum_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = treeFrac_lnd_mean_ens_anom[without_outliers_ix_wave00,],
ens2 = treeFrac_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = treeFrac_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = '%', main = 'treefrac'
)
lines(years, treeFrac_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = shrubFrac_lnd_mean_ens_anom[without_outliers_ix_wave00,],
ens2 = shrubFrac_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = shrubFrac_lnd_mean_ens_anom[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = '%', main = 'shrubfrac'
)
lines(years, shrubFrac_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, ens1 = baresoilFrac_lnd_mean_ens_anom[without_outliers_ix_wave00,],
ens2 = baresoilFrac_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = baresoilFrac_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = '%', main = 'baresoilfrac')
lines(years, baresoilFrac_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, c3PftFrac_lnd_mean_ens_anom[without_outliers_ix_wave00,],
ens2 = c3PftFrac_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = c3PftFrac_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = '%', main = 'c3PftFrac')
lines(years, c3PftFrac_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)
linePlotMultiEns(years = years, c4PftFrac_lnd_mean_ens_anom[without_outliers_ix_wave00,],
ens2 = c4PftFrac_lnd_mean_ens_anom_wave01[without_outliers_ix_wave01,],
ens3 = c4PftFrac_lnd_mean_ens_anom_wave01[level2a_ix_wave01, ],
col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
ylab = '%', main = 'c3PftFrac')
lines(years, c4PftFrac_lnd_mean_stan_anom, col = stancol, lty = 'solid', lwd = 2)
reset()
legend('bottomright', legend = c('wave00','wave01','wave01 level2','standard'), lty = 'solid', lwd = 1.5, col = c(lcol_wave0, lcol_wave01, lcol_wave01_level2, stancol), inset = c(0.05, 0.15) )

#dev.off()
Emulator fits
We hope that running the new ensemble gives us a better emulator, and allows us to rule out more input space. We particularly hope that the emulator is better for those members that are inside AW’s constraints.
First, we can look at the emulator errors in two cases: The level1a data (a basic carbon cycle), and then with the Wave01 data, which should have similar characteristics. (We should have eliminated really bad simulations, but wave01 is not constrained the data perfectly to be within AW constraints.)
## Emulator fit list of level 1a ensemble
#fit_list_const_level1a <- createKmFitList(X = X_level1a, Y = Y_const_level1a_scaled)
Y_const_level1a_scaled_list <- mat2list(Y_const_level1a_scaled)
fit_list_const_level1a <- mclapply(X = Y_const_level1a_scaled_list, FUN = km, formula = ~., design = X_level1a,
mc.cores = 4, control = list(trace = FALSE))
## Remove any timeseries outliers from the new wave and build emulators
# Bind together the input matrices and scaled output data
#X_level1a_wave01 <- rbind(X_level1a, X_wave01_train)[-440, ]
X_level1a_wave01 <- rbind(X_level1a, X_wave01_train[without_outliers_ix_wave01, ])
Y_const_level1a_wave01_scaled <- rbind(Y_const_level1a_scaled, Y_const_wave01_scaled[without_outliers_ix_wave01, ])
#apply(Y_const_level1a_wave01_scaled ,2, which.max)
#apply(Y_const_level1a_wave01_scaled ,2, which.min)
Found the outlier - looks like it’s 440
findOutliers <- function(x, sds = 6){
ix <- which(abs(x - mean(x)) > (sds * sd(x)))
}
apply(Y_const_level1a_wave01_scaled ,2, findOutliers, sds = 10)
integer(0)
# Create fit lists for the combined data
Y_const_level1a_wave01_scaled_list <- mat2list(Y_const_level1a_wave01_scaled)
fit_list_const_level1a_wave01 <- mclapply(X = Y_const_level1a_wave01_scaled_list , FUN = km, formula = ~., design = X_level1a_wave01,
mc.cores = 4, control = list(trace = FALSE))
Leave-one-out analyses of emulator prediction accuracy
loolist_km_Y_level1a <- mclapply(X = fit_list_const_level1a, FUN = leaveOneOut.km, type = 'UK', trend.reestim = TRUE)
loolist_km_Y_level1a_wave01 <- mclapply(X = fit_list_const_level1a_wave01, FUN = leaveOneOut.km, type = 'UK', trend.reestim = TRUE)
loostats_km_Y_level1a <- lapply(fit_list_const_level1a, FUN = kmLooStats)
loostats_km_Y_level1a_wave01 <- lapply(fit_list_const_level1a_wave01, FUN = kmLooStats)
The top row shows the leave-one-out prediction accuracy of the original wave00 ensemble, and the lower row the entire wave00 AND wave01 ensemble combined.
#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(2,4), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a)){
y <- Y_const_level1a_scaled[, i]
loo <- loolist_km_Y_level1a[[i]]
ylim <- range(c(loo$mean - (2*loo$sd), loo$mean + (2*loo$sd)) )
plot(y, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave00col, 100),
pch = 19)
segments(x0 = y, y0 = loo$mean - (2*loo$sd) , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave00col, 50))
abline(0,1)
legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2 )
legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)
}
mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2)
mtext('Level 1a ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)
#dev.off()
#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
for(i in 1:length(loolist_km_Y_level1a)){
y <- Y_const_level1a_wave01_scaled[, i]
loo <- loolist_km_Y_level1a_wave01[[i]]
ylim <- range(c(loo$mean - (2*loo$sd), loo$mean + (2*loo$sd)) )
plot(y, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave01col, 100),
pch = 19)
segments(x0 = y, y0 = loo$mean - (2*loo$sd) , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave01col, 100))
abline(0,1)
legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2 )
legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a_wave01[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)
}
mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2)
mtext('Level 1a ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)

# remove to level 1a Relative to toplevel_ix - useful for plotting etc.
#toplevel_to_level_1a_ix <-(toplevel_ix[-Y_nlevel0_ix])[level1a_ix]
# So further constraining to level 2 can be associated back to the top level.
level2_ix <- which(Y_const_level1a_scaled[,'nbp_lnd_sum'] > 0 &
Y_const_level1a_scaled[,'npp_nlim_lnd_sum'] > 35 & Y_const_level1a_scaled[,'npp_nlim_lnd_sum'] < 80 &
Y_const_level1a_scaled[,'cSoil_lnd_sum'] > 750 & Y_const_level1a_scaled[,'cSoil_lnd_sum'] < 3000 &
Y_const_level1a_scaled[,'cVeg_lnd_sum'] > 300 & Y_const_level1a_scaled[,'cVeg_lnd_sum'] < 800
)
level2_ix_level1a_wave01 <- which(Y_const_level1a_wave01_scaled[,'nbp_lnd_sum'] > 0 &
Y_const_level1a_wave01_scaled[,'npp_nlim_lnd_sum'] > 35 & Y_const_level1a_wave01_scaled[,'npp_nlim_lnd_sum'] < 80 &
Y_const_level1a_wave01_scaled[,'cSoil_lnd_sum'] > 750 & Y_const_level1a_wave01_scaled[,'cSoil_lnd_sum'] < 3000 &
Y_const_level1a_wave01_scaled[,'cVeg_lnd_sum'] > 300 & Y_const_level1a_wave01_scaled[,'cVeg_lnd_sum'] < 800
)
Emulator accuracy of members from wave 00 and wave 01 that pass level 2 (AW’s) constraints
We see that the error stats for some of the outputs from wave01 are worse, but there are many more ensemble members that lie within the constraints for wave 01.
“pmae” is “proportional mean absolue error”, which is the mean absolute error expressed as a percentage of the original (minimally constrained) ensemble range in that output.
#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(2,4), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a)){
y <- Y_const_level1a_scaled[level2_ix, i]
loo <- loolist_km_Y_level1a[[i]]
ylim <- range(c(loo$mean[level2_ix] - (2*loo$sd[level2_ix]), loo$mean[level2_ix] + (2*loo$sd[level2_ix])) )
plot(y, loo$mean[level2_ix], xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave00col, 100),
pch = 19)
segments(x0 = y, y0 = loo$mean[level2_ix] - (2*loo$sd[level2_ix]) , x1 = y , y1 = loo$mean[level2_ix] + (2*loo$sd[level2_ix]), col = makeTransparent(wave00col, 100))
abline(0,1)
legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2 )
legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)
}
#dev.off()
#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
for(i in 1:length(loolist_km_Y_level1a)){
y <- Y_const_level1a_wave01_scaled[level2_ix_level1a_wave01, i]
loo <- loolist_km_Y_level1a_wave01[[i]]
ylim <- range(c(loo$mean[level2_ix_level1a_wave01] - (2*loo$sd[level2_ix_level1a_wave01]), loo$mean[level2_ix_level1a_wave01] + (2*loo$sd[level2_ix_level1a_wave01])) )
plot(y, loo$mean[level2_ix_level1a_wave01], xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave01col, 100),
pch = 19)
segments(x0 = y, y0 = loo$mean[level2_ix_level1a_wave01] - (2*loo$sd[level2_ix_level1a_wave01]) , x1 = y , y1 = loo$mean[level2_ix_level1a_wave01] + (2*loo$sd[level2_ix_level1a_wave01]), col = makeTransparent(wave01col, 50))
abline(0,1)
legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2 )
legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a_wave01[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)
}
mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2)
mtext('Level 2 constrained ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)

Does the emulator improve is you look at only the 37 members that pass level 2 constraints in wave 00?
This gives us an idea of how good the emulator is where it really matters, and as the members are consistent, gives us a fairer idea of whether the emulators have improved with more members.
Good news is, the emulators are more accurate for wave01.
kmLooStatsSubset <- function (km, ix, type = "UK")
{
# Calculate summary statistics for a subset of the members of a km fit list
loo <- leaveOneOut.km(km, type = type, trend.reestim = TRUE)
preddiff <- loo$mean[ix] - km@y[ix]
mae <- mean(abs(preddiff))
rmse <- sqrt(mean(preddiff^2))
maxerr <- max(preddiff)
absdiff <- abs(diff(range(km@y)))
pmae <- (mae/absdiff) * 100
return(list(loo = loo, mae = mae, pmae = pmae, maxerr = maxerr))
}
loolist_km_Y_level1a_level2 <- rapply(loolist_km_Y_level1a, f = function(x) x[level2_ix], how = "list")
loolist_km_Y_level1a_wave01_level2 <- rapply(loolist_km_Y_level1a_wave01, f = function(x) x[level2_ix], how = "list")
#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(2,2), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a_level2)){
y <- Y_const_level1a_scaled[level2_ix, i]
loo <- loolist_km_Y_level1a_level2[[i]]
ylim <- range(c(loo$mean- (2*loo$sd), loo$mean + (2*loo$sd)) )
plot(y, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave00col, 250),
pch = 19)
arrows(x0 = y, y0 = loo$mean - (2*loo$sd) , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave00col, 150) , angle = 90, code = 3, length = 0.03)
y1 <- Y_const_level1a_wave01_scaled[level2_ix, i]
loo <- loolist_km_Y_level1a_wave01_level2[[i]]
points(y1, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave01col, 250),
pch = 19)
arrows(x0 = y, y0 = loo$mean - (2*loo$sd) , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave01col, 250), angle = 90, code = 3, length = 0.03)
abline(0,1)
legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2 )
legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)
}
mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2)
mtext('Level 2 wave 00 ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)
reset()
legend('topleft', pch = 19, legend = c('wave00', 'wave01'), col = c(wave00col, wave01col ), horiz = TRUE)

NA
NA
These leave-one-out prediction accuracy plots rank the ensemble members from largest underprediction to largest overprediction using the wave00 predictions. A perfect prediction would appear on the horizontal “zero” line.
Many of the wave01 predictions are closer to the horizontal line, and therefore more accurate predictions.
None of the predictions are outside the uncertainty bounds, which suggests they are overconservative (should be smaller).
#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(4,1), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a_level2)){
y <- Y_const_level1a_scaled[level2_ix, i]
loo_00 <- loolist_km_Y_level1a_level2[[i]]
loo_01 <- loolist_km_Y_level1a_wave01_level2[[i]]
preddiff_wave00 <- y - loo_00$mean
preddiff_wave01 <- y - loo_01$mean
# rank by the original wave 00 predictions
loo_rank_ix <- sort(preddiff_wave00 , index.return = TRUE)
ylim <- range(c(preddiff_wave00[loo_rank_ix$ix] - (2*loo_00$sd[loo_rank_ix$ix]),
preddiff_wave00[loo_rank_ix$ix] + (2*loo_00$sd[loo_rank_ix$ix]),
preddiff_wave01[loo_rank_ix$ix] - (2*loo_01$sd[loo_rank_ix$ix]),
preddiff_wave01[loo_rank_ix$ix] + (2*loo_01$sd[loo_rank_ix$ix])
)
)
plot(preddiff_wave00[loo_rank_ix$ix], xlab = '', ylab = '', main = '' , col = makeTransparent(wave00col, 255),
pch = 19, ylim = ylim)
abline(h = 0)
arrows(x0 = 1:length(y), y0 = preddiff_wave00[loo_rank_ix$ix] - (2*loo_00$sd[loo_rank_ix$ix]) , x1 = 1:length(y) , y1 = preddiff_wave00[loo_rank_ix$ix] + (2*loo_00$sd[loo_rank_ix$ix]), col = makeTransparent(wave00col, 150), angle = 90, code = 3, length = 0.03)
points(preddiff_wave01[loo_rank_ix$ix], xlab = '', ylab = '', main = '' , col = makeTransparent(wave01col, 255),
pch = 19)
arrows(x0 = 1:length(y), y0 = preddiff_wave01[loo_rank_ix$ix] - (2*loo_01$sd[loo_rank_ix$ix]) , x1 = 1:length(y) , y1 = preddiff_wave01[loo_rank_ix$ix] + (2*loo_01$sd[loo_rank_ix$ix]), col = makeTransparent(wave01col, 150), angle = 90, code = 3, length = 0.03)
mtext(colnames(Y_const_level1a_scaled)[i], side = 3, adj = 0, line = 1)
}
reset()
legend('topleft', pch = 19, legend = c('wave00', 'wave01'), col = c(wave00col, wave01col ), horiz = TRUE)

loostats_km_Y_level1a_sub <- lapply(fit_list_const_level1a, FUN = kmLooStatsSubset, ix = level2_ix)
loostats_km_Y_level1a_wave01_sub <- lapply(fit_list_const_level1a_wave01, FUN = kmLooStatsSubset, ix = level2_ix)
Looking at the proportional mean absolute error (pmae), expressed in percent, we can see that it doesn’t improve much for the whole ensemble, but does improve significantly for the subset of ensemble members that fall within AW’s constraints from the first ensemble (marked "_sub").
pmae_wave00 <- lapply(loostats_km_Y_level1a, FUN = function(x) x$pmae )
pmae_wave01 <- lapply(loostats_km_Y_level1a_wave01, FUN = function(x) x$pmae )
pmae_wave00_sub <- lapply(loostats_km_Y_level1a_sub, FUN = function(x) x$pmae )
pmae_wave01_sub <- lapply(loostats_km_Y_level1a_wave01_sub, FUN = function(x) x$pmae )
pmae_table <- cbind(pmae_wave00, pmae_wave01, pmae_wave00_sub, pmae_wave01_sub)
print(pmae_table)
pmae_wave00 pmae_wave01 pmae_wave00_sub pmae_wave01_sub
[1,] 5.034869 4.925423 7.61508 4.290347
[2,] 4.229698 3.839264 4.757647 3.803702
[3,] 3.613128 3.612911 4.697644 3.638093
[4,] 4.225332 4.445741 4.727078 3.679449
Comparing atmospheric growth in wave00, wave01 and observations
obscol = 'purple'
wave01_level2col <- 'gold'
ag <- matrix(nrow = nrow(nbp_ens), ncol = ncol(nbp_ens))
ag01 <- matrix(nrow = nrow(nbp_ens_wave01), ncol = ncol(nbp_ens_wave01))
for(i in 1:nrow(nbp_ens)){
ag[i, ] <- historical_carbon_budget$`fossil emissions excluding carbonation`[resid_ix] - historical_carbon_budget$`ocean sink`[resid_ix] - nbp_ens[i, ]
}
for(i in 1:nrow(nbp_ens_wave01)){
ag01[i, ] <- historical_carbon_budget$`fossil emissions excluding carbonation`[resid_ix] - historical_carbon_budget$`ocean sink`[resid_ix] - nbp_ens_wave01[i, ]
}
ag_stan <- historical_carbon_budget$`fossil emissions excluding carbonation`[resid_ix] - historical_carbon_budget$`ocean sink`[resid_ix] - nbp_stan
#pdf(width = 8, height = 6, file = 'ag.pdf')
par(las = 1)
matplot(years, t(ag[without_outliers_ix_wave00, ]), type = 'l', lty = 'solid',ylim = c(-2, 10), col = makeTransparent(wave00col,100),
ylab = 'GtC', main = 'atmospheric growth', xlab = '',
bty = 'n', xlim = c(1960, 2013))
matlines(years, t(ag01[without_outliers_ix_wave01, ]), col = makeTransparent(wave01col,100), lty = 'solid')
matlines(years, t(ag01[level2a_ix_wave01, ]), col = makeTransparent(wave01_level2col, 100) , lty = 'solid')
lines(historical_carbon_budget$Year, historical_carbon_budget$`atmospheric growth`, col = obscol, lwd =2)
lines(years, ag_stan, col = stancol, lwd =2)
legend('topleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard', 'GCB'), col = c(wave00col, wave01col, wave01_level2col, stancol, obscol), lty = 'solid', lwd = 2)

#dev.off()
Andy asks - what constraint does that give us in cumulative NBP?
cumulative_nbp_ens <- t(apply(nbp_ens, 1, cumsum))
cumulative_nbp_ens_wave01 <- t(apply(nbp_ens_wave01, 1, cumsum))
cumulative_nbp_stan <- cumsum(nbp_stan)
matplot(years, t(cumulative_nbp_ens), type = 'l', lty = 'solid',ylim = c(-130, 250), col = makeTransparent(wave00col,150),
ylab = 'GtC', main = 'cumulative NBP', xlab = '',
bty = 'n', xlim = c(1850, 2020))
matlines(years, t(cumulative_nbp_ens_wave01),
col = makeTransparent(wave01col,150),
lty = 'solid')
matlines(years, t(cumulative_nbp_ens_wave01[level2_ix_wave01, ]),
col = makeTransparent(wave01_level2col, 150),
lty = 'solid')
lines(years, cumulative_nbp_stan, col = makeTransparent(stancol, 200), lty = 'solid', lwd = 1.5 )
legend('topleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), lty = 'solid', lwd = 1.5)

Eddy suggests measuring cumulative NBP against atmospheric growth rate
# Cumulative NBP at the end of the run
cnbp_modern_ens <- apply(cumulative_nbp_ens[, 135:164], 1, mean)
cnbp_modern_wave01 <- apply(cumulative_nbp_ens_wave01[, 135:164], 1, mean)
cnbp_modern_stan <- mean(cumulative_nbp_stan[135:164])
Calculate the atmospheric growth rate of 1984- 2013 using a simple linear fit
# agr = atmospheric growth rate
# agiav = atmospheric growth interannual variability
agr_modern_ens <- rep(NA, length = nrow(ag))
agiav_modern_ens <- rep(NA, length = nrow(ag))
for(i in 1:nrow(ag)){
dat <- data.frame(t =1:30, ag = ag[i, 135:164])
fit <- lm(ag ~ t, data = dat)
agr_modern_ens[i] <- coef(fit)['t']
agiav_modern_ens[i] <- sd(fit$residuals)
}
agr_modern_wave01 <- rep(NA, length = nrow(ag01))
agiav_modern_wave01 <- rep(NA, length = nrow(ag01))
for(i in 1:nrow(ag01)){
dat <- data.frame(t =1:30, ag = ag01[i, 135:164])
fit <- lm(ag ~ t, data = dat)
agr_modern_wave01[i] <- coef(fit)['t']
agiav_modern_wave01[i] <- sd(fit$residuals)
}
dat <- data.frame(t =1:30, ag = ag_stan[135:164])
agr_stan_fit <- lm(ag ~ t, data = dat)
agr_stan <- coef(agr_stan_fit)['t']
agiav_stan <- sd(agr_stan_fit$residuals)
plot(agr_modern_ens, cnbp_modern_ens, col = makeTransparent(wave00col,150),
ylim = c(-120,220), pch = 19,
xlab = 'Atmospheric growth rate 1984-2014',
ylab = 'Cumulative NBP from preindustrial by 1984-2013'
)
cnbp_wave01_cols <- rep(wave01col, length(cnbp_modern_wave01))
cnbp_wave01_cols[level2_ix_wave01] <- wave01_level2col
points( agr_modern_wave01,cnbp_modern_wave01, col = makeTransparent(cnbp_wave01_cols, 150), pch = 19)
points( agr_stan,cnbp_modern_stan, col = stancol, pch = 19, cex = 1.5)
legend('topleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), pch = 19)

print(c('correlation agr vs cnbp (all members)', cor(agr_modern_ens, cnbp_modern_ens)))
[1] "correlation agr vs cnbp (all members)" "-0.0410843006467242"
print(c('correlation agr vs cnbp (wave01)', cor(agr_modern_wave01, cnbp_modern_wave01)))
[1] "correlation agr vs cnbp (wave01)" "0.00250625692520855"
fit_wave00 <- lm(agr_modern_ens ~ cnbp_modern_ens)
fit_wave01 <- lm(agr_modern_wave01 ~ cnbp_modern_wave01)
print(summary(fit_wave00))
Call:
lm(formula = agr_modern_ens ~ cnbp_modern_ens)
Residuals:
Min 1Q Median 3Q Max
-0.047201 -0.003654 0.003930 0.006844 0.023340
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.112e-01 5.219e-04 213.034 <2e-16 ***
cnbp_modern_ens -1.404e-09 1.532e-09 -0.917 0.36
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.01165 on 497 degrees of freedom
Multiple R-squared: 0.001688, Adjusted R-squared: -0.0003208
F-statistic: 0.8403 on 1 and 497 DF, p-value: 0.3598
print(summary(fit_wave01))
Call:
lm(formula = agr_modern_wave01 ~ cnbp_modern_wave01)
Residuals:
Min 1Q Median 3Q Max
-3476 -3476 -3476 -3476 1383498
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3.476e+03 3.476e+03 1.00 0.318
cnbp_modern_wave01 4.103e-29 8.205e-28 0.05 0.960
Residual standard error: 69440 on 398 degrees of freedom
Multiple R-squared: 6.281e-06, Adjusted R-squared: -0.002506
F-statistic: 0.0025 on 1 and 398 DF, p-value: 0.9601
Interannual variability and cumulative NBP
(correlations are close to zero, especially in the later wave)
plot(agiav_modern_ens, cnbp_modern_ens, col = makeTransparent(wave00col,150),
ylim = c(-120,220), pch = 19,
xlab = 'Atmospheric growth IAV 1984-2014',
ylab = 'Cumulative NBP from preindustrial by 1984-2013'
)
points( agiav_modern_wave01, cnbp_modern_wave01, col = makeTransparent(cnbp_wave01_cols, 150), pch = 19)
points( agiav_stan,cnbp_modern_stan, col = stancol, pch = 19, cex = 1.5)
legend('bottomright', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), pch = 19)

print(cor(agiav_modern_ens, cnbp_modern_ens))
[1] 0.03248668
print(cor( agiav_modern_wave01, cnbp_modern_wave01))
[1] 0.002506268
How close can we get the model to reality?
Using Atmospheric Growth Rate as an example, how close can we get the model to observations? Can we do better than standard? What are the trade offs of doing so? How does getting close in AGR affect performance in other outputs?
# Define the observed atmospheric growth rate.
ag_obs <- historical_carbon_budget$`atmospheric growth`[which(historical_carbon_budget$Year %in% years)]
# Model departure from observations of atmospheric growth
ag_err <- sweep(ag, 2, ag_obs, FUN = '-')
ag01_err <- sweep(ag01, 2, ag_obs, FUN = '-')
long_modern_years <- 1960:2013
ag_modern_ix <- which( years %in% long_modern_years)
ag_err_modern <- ag_err[, ag_modern_ix]
ag01_err_modern <- ag01_err[, ag_modern_ix]
ag_err_stan <- ag_stan - ag_obs
ag_err_stan_modern <- ag_err_stan[ag_modern_ix]
matplot(long_modern_years, t(ag_err_modern[without_outliers_ix_wave00,]), type = 'l', lty = 'solid', col = makeTransparent(wave00col, 100), main = 'Amospheric Growth Error')
errcol <- rep(wave01col, nrow(ag01[without_outliers_ix_wave01,]))
#errcol[level2_ix_wave01] <- wave01_level2col
matlines(long_modern_years, t(ag01_err_modern[without_outliers_ix_wave01,]), col = makeTransparent(errcol, 100), lty = 'solid')
matlines(long_modern_years, t(ag01_err_modern[level2a_ix_wave01, ]), col = makeTransparent(wave01_level2col,100), lty = 'solid')
lines(long_modern_years, ag_err_stan_modern, col = stancol)
legend('bottomleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), lty = 'solid', lwd = 1.5)
abline(h=0)

# Atmospheric growth mean error
ag_modern_me <- apply(ag_err_modern,1,mean)
ag01_modern_me <- apply(ag01_err_modern,1, mean)
ag_modern_me_stan <- mean(ag_err_stan[ag_modern_ix])
# Mean absolute error
ag_modern_mae <- apply(abs(ag_err_modern),1,mean)
ag01_modern_mae <- apply(abs(ag01_err_modern),1, mean)
ag_modern_mae_stan <- mean(abs(ag_err_stan[ag_modern_ix]))
# Root mean square error
ag_modern_rmse <- apply(ag_err_modern,1, function(x) sqrt(mean(x^2)))
ag01_modern_rmse <- apply(ag01_err_modern,1, function(x) sqrt(mean(x^2)))
ag_modern_rmse_stan <- sqrt(mean(ag_err_stan[ag_modern_ix]^2))
# There are some big outliers
outlier_ix_wave01 <- which(abs(ag01_modern_me )> 100)
par(mfrow = c(2,1))
hist(ag_modern_me, main = 'Atmospheric growth mean error', col = wave00col, xlim = c(-3, 3))
rug(ag_modern_me_stan, col = stancol, lwd = 2)
hist(ag01_modern_me[-outlier_ix_wave01], col = wave01col, xlim = c(-3,3), main = '')
rug(ag_modern_me_stan, col = stancol, lwd = 2)
par(mfrow = c(2,1))

hist(ag_modern_mae, main = 'Atmospheric growth mean absolute error', col = wave00col, xlim = c(0, 3))
rug(ag_modern_mae_stan, col = stancol, lwd = 2)
hist(ag01_modern_mae[-outlier_ix_wave01], col = wave01col, lwd = 2, xlim = c(0,3), main = '')
rug(ag_modern_mae_stan, col = stancol, lwd = 2)
par(mfrow = c(2,1))

hist(ag_modern_rmse, xlim = c(0,3), col = wave00col, main = 'Atmospheric growth RMSE')
rug(ag_modern_rmse_stan, col = stancol, lwd = 2 )
hist(ag01_modern_rmse[-outlier_ix_wave01], col = wave01col, xlim = c(0,3), main = '')
rug(ag_modern_rmse_stan, col = stancol, lwd = 2 )

NA
NA
NA
We’ve established that most of the original ensemble have an ME/MAE/RMSE larger than the standard run. More (but few) of the wave01 perform better than standard.
better_ix_ag_rmse <- which(ag_modern_rmse < ag_modern_rmse_stan)
better_ix_ag01_rmse <- which(ag01_modern_rmse < ag_modern_rmse_stan)
X_better_ag <- rbind(X[better_ix_ag_rmse, ], X_wave01_train[better_ix_ag01_rmse, ])
A map of the 2D projections of parameter space where the ensemble member performs better than standard.
The blue part is the first wave, and not subject to constraint so may be removed in the second wave (wave01).
better_ix <- 1:nrow(X_better_ag)
better_cols <- c(rep(wave00col, length(better_ix_ag_rmse)), rep(wave01col,length(better_ix_ag01_rmse)))
pairs(X_better_ag,
col = better_cols,
gap = 0,
xlim = c(0,1), ylim = c(0,1),
pch = 19,
cex= 0.8,
lower.panel = NULL)

NA
NA
NA
Build emulators and find parts of parameter space that to better than standard at atmospheric growth.
Having trouble fitting RMSE, to trying mean error.
Why is there an odd collection at just under 1?
# there are 100 wave01 ensemble members that pass the level 2 constrainst
length(level2_ix_wave01)
[1] 128
better_ix_ag_me <- which(abs(ag_modern_me) < abs(ag_modern_me_stan))
# There are 104 that have a smaller mean error than standard
better_ix_ag01_me <- which(abs(ag01_modern_me) < abs(ag_modern_me_stan))
# There are only 41 that pass level 2 AND have smaller error than standard
level2_and_better_ag01_ix <- intersect(level2_ix_wave01, better_ix_ag01_me)
This next pairs plot looks at all the ensemble members that have a better mean atmospheric growth error than standard.
X_better_ag <- rbind(X[better_ix_ag_me, ], X_wave01_train[better_ix_ag01_me, ])
better_ix <- 1:nrow(X_better_ag)
better_cols <- c(makeTransparent(rep(wave00col, length(better_ix_ag_me)), 100), makeTransparent(rep(wave01col,length(better_ix_ag01_me)), 100))
pairs(X_better_ag,
col = better_cols,
gap = 0,
xlim = c(0,1), ylim = c(0,1),
pch = 19,
cex= 0.8,
lower.panel = NULL)

This next plot looks at all the ensemble members that have a better mean atmospheric growth error than standard AND pass the level 2 constraints.
The number is small (41/300), but the ensemble members seem spread across parameter space.
X_level2_and_better_ag01 <- X[level2_and_better_ag01_ix, ]
pairs(X_level2_and_better_ag01,
col = makeTransparent('black', 150),
gap = 0,
xlim = c(0,1), ylim = c(0,1),
pch = 19,
cex= 0.8,
lower.panel = NULL)

plot(fit_ag_me)

library(emtools)
library(imptools)
library(viztools)
nunif <- 10000
X_unif <- samp_unif(nunif, mins = rep(0,32), maxes = rep(1, 32))
colnames(X_unif) <- colnames(X)
pred_unif_ag <- predict.km(fit_ag_me, newdata = X_unif, type = 'UK' )
# Keeps about 20%
X_kept_ix <- which(abs(pred_unif_ag$mean) < abs(ag_modern_me_stan))
(length(X_kept_ix) / nunif) * 100
[1] 21.42
# this is 8%
#X_kept_ix <- which(abs(pred_unif$mean) < 0.1)
Input space with low Atmospheric Growth Error
This pairs plot shows the 2d and marginal density of emulated input points where the emulated atmospheric growth is closer to the observations than the standard model.
This technique might provide a useful set of points for optimising the model (at least to atmospheric growth).
#pairs(X_unif[X_kept_ix, ][1:50,],
# col = makeTransparent('black',50),
# gap = 0,
# xlim = c(0,1), ylim = c(0,1),
# pch = 19,
# cex= 0.8,
# lower.panel = NULL)
par(oma = c(0,0,0,3), bg = 'white')
panel.hist = function(x, ...) {
usr = par("usr"); on.exit(par(usr))
par(usr = c(usr[1:2], 0, 1.5))
hist(x, freq = FALSE, col="cyan", add=TRUE)
lines(density(x))
}
pairs(X_unif[X_kept_ix, ],
# labels = 1:d,
gap = 0, lower.panel = NULL, xlim = c(0,1), ylim = c(0,1),
panel = dfunc_up,
diag.panel = panel.hist,
cex.labels = 1,
col.axis = 'white',
dfunc_col = rb)

Next, check emulators of all the other outputs and apply the constraints to them. See how the constraints change.
# First, try the emulators built using just wave01
#fit_list_const_level1a
Y_const_pred_unif_mean <- matrix(NA, ncol = ncol(Y_const_level1a_scaled), nrow = nrow(X_unif))
colnames(Y_const_pred_unif_mean) <- colnames(Y_const_level1a_scaled)
Y_const_pred_unif_sd <- matrix(NA, ncol = ncol(Y_const_level1a_scaled), nrow = nrow(X_unif))
colnames(Y_const_pred_unif_sd) <- colnames(Y_const_level1a_scaled)
for(i in 1:length(fit_list_const_level1a)){
pred_unif <- predict.km(object=fit_list_const_level1a[[i]], newdata = X_unif, type = 'UK')
Y_const_pred_unif_mean[,i ] <- pred_unif$mean
Y_const_pred_unif_sd[,i ] <- pred_unif$sd
}
Exploring further constraints
It’s pretty clear that the “initial” (level2) constraints can be improved on. How about tree, shrub and bare soil fractions?
Build an ensemble of modern vegetation fractions.
# What if we also had a "tolerance to error" that looked like this. Basically making up for the fact we haven't got "strong" observations of fractions, but might have a tolerance to error of some of these properties. They would at least allow us to identify trade-offs.
# treefrac 20- 40
# shrubfrac < 5
# baresoil < 40
#design = X_wave01_train[-ag01_outlier_ix, ],
# need to
par(mfrow = c(5,1), mar = c(2,2,2,1))
# bind ensemble 0 (level 1a) and wave01 fractions
Y_frac_level1a_wave01 <- rbind(Y_level1a[, y_names_frac_mean], ens_wave01_frac_mean_mv$datmat[without_outliers_ix_wave01 , ])
frac_hist_breaks <- seq(from = 0, to = 100, by = 5)
for(i in 1:ncol(Y_frac_level1a_wave01))
{
cnam <- colnames(Y_frac_level1a_wave01)[i]
hist(Y_frac_level1a_wave01[1:nrow(Y_level1a),i], main = colnames(Y_frac_level1a_wave01)[i],
xlab = '', xlim = c(0,100), breaks = frac_hist_breaks, ylim = c(0,300), col = makeTransparent(wave00col,100))
hist(Y_frac_level1a_wave01[(nrow(Y_level1a)+1) : nrow(Y_frac_level1a_wave01), i],
add = TRUE, col = makeTransparent(wave01col,100), breaks = frac_hist_breaks )
abline(v = standard_modern_value[cnam])
}

Build emulators for the fraction data
# Create fit lists for the combined data
Y_frac_level1a_wave01_list <- mat2list(Y_frac_level1a_wave01)
fit_list_frac_level1a_wave01 <- mclapply(X = Y_frac_level1a_wave01_list, FUN = km, formula = ~., design = X_level1a_wave01,
mc.cores = 4, control = list(trace = FALSE))
It might be useful to try emulating log (fractions)
for(i in 1:length(fit_list_frac_level1a_wave01)){
plot(fit_list_frac_level1a_wave01[[i]])
}





NA
NA
Leave-one-out analyses of emulator prediction accuracy
loolist_km_Y_frac_level1a_wave01 <- mclapply(X = fit_list_frac_level1a_wave01, FUN = leaveOneOut.km, type = 'UK', trend.reestim = TRUE)
Y_frac_pred_unif_mean <- matrix(NA, ncol = ncol(Y_frac_level1a_wave01), nrow = nrow(X_unif))
colnames(Y_frac_pred_unif_mean) <- colnames(Y_frac_level1a_wave01)
Y_frac_pred_unif_sd <- matrix(NA, ncol = ncol(Y_frac_level1a_wave01), nrow = nrow(X_unif))
colnames(Y_frac_pred_unif_sd) <- colnames(Y_frac_level1a_wave01)
for(i in 1:length(fit_list_frac_level1a_wave01)){
pred_unif <- predict.km(object = fit_list_frac_level1a_wave01[[i]], newdata = X_unif, type = 'UK')
Y_frac_pred_unif_mean[,i ] <- pred_unif$mean
Y_frac_pred_unif_sd[,i ] <- pred_unif$sd
}
# treefrac 20- 40
# shrubfrac < 5
# baresoil < 40
treefrac_ix_em <- which(Y_frac_pred_unif_mean[, 'treeFrac_lnd_mean'] > 20 & Y_frac_pred_unif_mean[, 'treeFrac_lnd_mean'] < 40)
# Y_const_pred_unif_mean[,'nbp_lnd_sum'] > 0 &
# Y_const_pred_unif_mean[,'npp_nlim_lnd_sum'] > 35 & Y_const_pred_unif_mean[,'npp_nlim_lnd_sum'] < 80 &
# Y_const_pred_unif_mean[,'cSoil_lnd_sum'] > 750 & Y_const_pred_unif_mean[,'cSoil_lnd_sum'] < 3000 &
# Y_const_pred_unif_mean[,'cVeg_lnd_sum'] > 300 & Y_const_pred_unif_mean[,'cVeg_lnd_sum'] < 800 &
# & &
#abs(pred_unif_ag$mean) < abs(ag_modern_me_stan)
# )
pairs(rbind(X_unif[treefrac_ix_em, ], X_stan_norm),
# labels = 1:d,
gap = 0, lower.panel = NULL, xlim = c(0,1), ylim = c(0,1),
panel = dfunc_up_truth,
diag.panel = panel.hist,
cex.labels = 1,
col.axis = 'white',
dfunc_col = rb)

NA
NA
(length(treefrac_ix_em) / nunif) * 100
[1] 18.48
treefrac_ix_em <- which(Y_frac_pred_unif_mean[, 'treeFrac_lnd_mean'] > 20 & Y_frac_pred_unif_mean[, 'treeFrac_lnd_mean'] < 40)
Sequentially add constraints
# use get or assign?
# use eval and parse
eval(parse(text="5+5"))
[1] 10
createConstraintString <- function(yvec, mins, maxes){
# This function constructs a logical expression as a string to be evaluated
out <- 'which('
for(i in 1:length(yvec)){
if(i<length(yvec)){
subconst <- paste0('Y_unif','[,', '"',yvec[i],'"',']', '>', mins[i], '&', 'Y_unif','[,', '"',yvec[i], '"',']' , '<', maxes[i], '&')
}
else{
subconst <- paste0('Y_unif','[,', '"',yvec[i],'"',']', '>', mins[i], '&', 'Y_unif','[,', '"',yvec[i], '"',']' , '<', maxes[i])
}
out <- paste0(out, subconst)
}
out <- paste0(out, ')')
}
yvec <- colnames(Y_frac_pred_unif_mean)
test <- createConstraintString(yvec, mins = c(-20,-20,-20,-20,-20), maxes = c(100,100,100,100,100))
#eval(parse(text = paste0()))
inputConstraintSize <- function(Y_unif, yvec, mins, maxes){
# Calculate the indices of Y_unif that are within the bounds set by mins and maxes
ix_kept <- eval(parse(text = createConstraintString(yvec=yvec, mins = mins, maxes = maxes)))
prop_kept <- length(ix_kept) / nrow(Y_unif)
return(list(ix_kept = ix_kept, prop_kept = prop_kept))
}
# construct a statement
individual constraint
pdf(file = 'figs/individual_constraint.pdf')
par(mar = c(14, 4, 2, 1), las = 1)
plot(1:9, perc_kept_individual, ylim = c(0,100), type = 'b', axes = FALSE, xlab = '',
ylab = 'Proportion of input space retained (%)')
axis(2)
axis(1, labels = paste0(colnames(Y_pred), ' (',Y_pred_mins,'-', Y_pred_maxes,')'), at = 1:9, las = 2)
dev.off()
null device
1
Sequential constraint
Each constraint is added to the previous constraint
pdf(file = 'figs/sequential_constraint.pdf')
par(mar = c(14, 4, 2, 1), las = 1)
plot(1:9, perc_kept, ylim = c(0,100), type = 'b', axes = FALSE, xlab = '',
ylab = 'Proportion of input space retained (%)')
axis(2)
axis(1, labels = paste0(colnames(Y_pred), ' (',Y_pred_mins,'-', Y_pred_maxes,')'), at = 1:9, las = 2)
dev.off()
null device
1
How far from data is the model?
frac_cci <- c(global_frac_cci[1] + global_frac_cci[2], global_frac_cci[3], global_frac_cci[4], global_frac_cci[5], global_frac_cci[8] )
frac_mv <- c(standard_modern_value['treeFrac_lnd_mean'], standard_modern_value['c3PftFrac_lnd_mean'], standard_modern_value['c4PftFrac_lnd_mean'], standard_modern_value['shrubFrac_lnd_mean'], standard_modern_value['baresoilFrac_lnd_mean'] )
fraclab = c('Trees', 'C3 grasses', 'C4 grasses', 'Shrubs', 'Bare soil')
par(las = 1, mar = c(8, 4,3,2))
plot(1:5, frac_cci*100, ylim = c(0,50), pch = 19, cex = 1.5, axes = FALSE)
axis(1, at = 1:5, labels = fraclab, las = 2)
axis(2)
points(1:5, frac_mv, col = 'red', pch = 19, cex = 1.5)
legend('topleft', legend = c('LC CCI', 'JULES-ES-1p0'), col = c('black', 'red'), pch = c(19,19))

NA
NA
NA
How similar are each of the constraints?
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgSlVMRVMxcDAgZW5zZW1ibGUgd2F2ZTAxIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwotLS0KCiMgSW50cm9kdWN0aW9uCgpBIGNvbXBhcmlzb24gb2YgSlVMRVMtRVMtMXAwIHdhdmUwMSBtZW1iZXJzIGFnYWluc3QgdGhlIG9yaWdpbmFsIGVuc2VtYmxlICh3YXZlMDApLgoKV2F2ZSAwMSBpbnB1dCBwYXJhbWV0ZXIgc2V0cyB3ZXJlIHBpY2tlZCB1c2luZyBIaXN0b3J5IG1hdGNoaW5nIHRvIGZhbGwgd2l0aGluIEFuZHkgV2lsdHNoaXJlJ3MgYmFzaWMgY29uc3RyYWludHMgb24gTkJQLCBOUFAsIGNTb2lsIGFuZCBjVmVnIHN0b2NrcyBhdCB0aGUgZW5kIG9mIHRoZSAyMHRoIGNlbnR1cnkuIFdlIHVzZSAzMDAgb2YgdGhlIDUwMCBtZW1iZXJzLCBrZWVwaW5nIGJhY2sgMi81dGhzIGZvciBlbXVsYXRvciB2YWxpZGF0aW9uIGxhdGVyLgoKV2UgYW5zd2VyIHNvbWUgYmFzaWMgcXVlc3Rpb25zLgoKV2hhdCBwcm9wb3J0aW9uIG9mIHRoZSBuZXcgZW5zZW1ibGUgbWF0Y2ggQVcncyBjb25zdHJhaW50cz8KCldoYXQgZG8gdGhlIHRpbWVzZXJpZXMgb2YgY2FyYm9uIGN5Y2xlIHByb3BlcnRpZXMgbG9vayBsaWtlIHdpdGggYW5kIHdpdGhvdXQgQVcncyBjb25zdHJhaW50cz8KCkhvdyBnb29kIGlzIGEgR1AgZW11bGF0b3I/IERvZXMgaXQgZ2V0IGJldHRlciBvdmVyYWxsIHdpdGggdGhlIG5ldyBlbnNlbWJsZSBtZW1iZXJzIGFkZGVkPyBJbiBwYXJ0aWN1bGFyLCBkb2VzIGl0IGdldCBiZXR0ZXIgZm9yIHRob3NlIG1lbWJlcnMgd2l0aGluIHRoZSBBVyBjb25zdHJhaW50cz8KCkRvZXMgY29tcGFyaXNvbiBvZiB0aGUgZW5zZW1ibGUgd2l0aCBBdG1vc3BoZXJpYyBncm93dGggb2JzZXJ2YXRpb25zIGdpdmUgbW9yZSBvZiBhIGNvbnN0cmFpbnQ/CgpUbyBkbzoKCkRvZXMgdGhlIHNlbnNpdGl2aXR5IGFuYWx5c2lzIGNoYW5nZT8KCiMgUHJlbGltaW5hcmllcwpMb2FkIGxpYnJhcmllcywgZnVuY3Rpb25zIGFuZCBkYXRhLgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzID0gJ2hpZGUnfQojIExvYWQgaGVscGVyIGZ1bmN0aW9ucwoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5wYXRoID0gImZpZ3MvIiwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmdzID0gRkFMU0UpCgojIGxvYWQgaGVscGVyIGZ1bmN0aW9ucywgZGF0YSBhbmQgZG8gcHJlbGltaW5hcnkgcHJvY2Vzc2luZyBvZiB0aGUgZW5zZW1ibGUuCnNvdXJjZSgnSlVMRVMtRVMtMXAwLWNvbW1vbi5SJykKCmBgYAoKYGBge3J9CgojZW5zbG9jX3dhdmUwMSA8LSAnL3NjcmF0Y2gvaGFkYXcvanVsZXNfcG9zdHByb2Nlc3MvdS1jazAwNi8nCmVuc2xvY193YXZlMDEgPC0gJy9kYXRhL3VzZXJzL2hhZGF3L0pVTEVTX0VTX1BQRS91LWNrMDA2LycKCiMgTnVtYmVyIG9mIGVuc2VtYmxlIG1lbWJlcnMgKG91dCBvZiA1MDApIHRvIHVzZSBmb3IgdHJhaW5pbmcgaW4gd2F2ZTAxCm50cmFpbl93YXZlMDEgPC0gNDAwCgpgYGAKCgpgYGB7cn0KCm1ha2VKdWxlc0Vuc2VtYmxlTW9kZXJuVmFsdWUgPC0gZnVuY3Rpb24oZW5zbG9jLCB2YXJsaXN0LCBuc3RhcnQsIG5lbmQsIGl4ID0gMTQ0OjE2NCl7CgogIG5lbnMgPC0gKG5lbmQgLSBuc3RhcnQpICsgMQogIGRhdG1hdCA8LSBtYXRyaXgobnJvdyA9IG5lbnMsIG5jb2wgPSBsZW5ndGgodmFybGlzdCkpCiAgY29sbmFtZXMoZGF0bWF0KSA8LSB2YXJsaXN0CgogIGVuc2xpc3QgPC0gcGFzdGUoIlAiLCBmb3JtYXRDKG5zdGFydDpuZW5kLCB3aWR0aD00LCBmbGFnPSIwIiksIHNlcD0iIikKCiAgZm9yKGkgaW4gMTpuZW5zKXsKCiAgICB2ZWMgPC0gcmVwKE5BLCBsZW5ndGgodmFybGlzdCkpCgogICAgZW5zbWVtYmVyIDwtIGVuc2xpc3RbaV0KCiAgICBmbiA8LSBwYXN0ZTAoZW5zbG9jLCdKVUxFUy1FUy0xcDBfJyxlbnNtZW1iZXIsJ19Bbm51YWxfZ2xvYmFsLm5jJykKCiAgICB0cnkobmMgPC0gbmNfb3BlbihwYXN0ZTAoZm4pKSkKICAgIHRyeSh2ZWMgPC0gc2FwcGx5KHZhcmxpc3QsIEZVTiA9IG1vZGVyblZhbHVlLCBuYyA9IG5jLCBpeCA9IGl4KSkKICAgIGRhdG1hdFtpLCBdIDwtIHZlYwogICAgbmNfY2xvc2UobmMpCiAgfQogIHJldHVybihsaXN0KGRhdG1hdCA9IGRhdG1hdCwgZW5zbGlzdCA9IGVuc2xpc3QpKQp9CgpgYGAKCgpgYGB7cn0KbnN0YXJ0IDwtIDQ5OQpuZW5kIDwtIChuc3RhcnQgKyBudHJhaW5fd2F2ZTAxKSAtIDEKCmlmIChmaWxlLmV4aXN0cygiZW5zZW1ibGVfd2F2ZTAxXzIwMjItMDQtMDgucmRhdGEiKSkgewogIGxvYWQoImVuc2VtYmxlX3dhdmUwMV8yMDIyLTA0LTA4LnJkYXRhIikKfSBlbHNlIHsKICAKZW5zX3dhdmUwMV9tdiA8LSBtYWtlSnVsZXNFbnNlbWJsZU1vZGVyblZhbHVlKGVuc2xvYyA9IGVuc2xvY193YXZlMDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFybGlzdCA9IHlfbmFtZXNfc3VtLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnN0YXJ0ID0gbnN0YXJ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmVuZCA9IG5lbmQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXggPSAxNDQ6MTY0KSAKICAKICBzYXZlKGVuc193YXZlMDFfbXYsIGZpbGUgPSJlbnNlbWJsZV93YXZlMDFfMjAyMi0wNC0wOC5yZGF0YSIpCn0KICAKYGBgCgpgYGB7cn0KCmxoc193YXZlMDEgPC0gcmVhZC50YWJsZSggJy4uL2NvbmZfZmlsZXNfYXVnbWVudF9KVUxFUy1FUy0xcDAvbGhzX2V4YW1wbGUudHh0JywgaGVhZGVyID0gVFJVRSkKClhfd2F2ZTAxID0gbm9ybWFsaXplKGxoc193YXZlMDEsIHdydCA9IHJiaW5kKGxoc19pLCBsaHNfaWksIGxoc193YXZlMDEpKQpjb2xuYW1lcyhYX3dhdmUwMSkgPSBjb2xuYW1lcyhsaHNfd2F2ZTAxKQoKIyBNYXRjaCB0aGUgNDAwIG91dHB1dHMgd2UncmUgdXNpbmcgaW4gdGhlIHRyYWluaW5nIGRhdGEKWF93YXZlMDFfdHJhaW4gPC0gWF93YXZlMDFbMTpudHJhaW5fd2F2ZTAxLCBdCgpgYGAKCmBgYHtyfQojIE1vZGVybiB2YWx1ZXMgdGhhdCB3ZSB1c2UgZm9yIGNvbnN0cmFpbnRzCgpZX2NvbnN0X3dhdmUwMSA8LSBlbnNfd2F2ZTAxX212JGRhdG1hdFssIHluYW1lc19jb25zdF0KWV9jb25zdF93YXZlMDFfc2NhbGVkIDwtIHN3ZWVwKFlfY29uc3Rfd2F2ZTAxLCAyLCBTVEFUUyA9IHNjYWxldmVjLCBGVU4gPSAnLycgKQoKCmBgYAoKIyMgSG93IG1hbnkgcnVuIGZhaWx1cmVzIHdlcmUgdGhlcmU/ICAKClRoZXJlIGFyZSBubyBOQXMgYnV0IHNvbWUgcmVsYXRpdmUgaHVtaWRpdHkgdmFsdWVzIGFyZSBpbmZpbml0ZS4KVGhlcmUgYXJlIG5vICJsb3cgTlBQIiBlbnNlbWJsZSBtZW1iZXJzCgpgYGB7cn0KCmxvd19ucHBfaXhfd2F2ZTAxIDwtIHdoaWNoKGVuc193YXZlMDFfbXYkZGF0bWF0WywnbnBwX25saW1fbG5kX3N1bSddIDwgMWU1KQoKbWluKGVuc193YXZlMDFfbXYkZGF0bWF0WywnbnBwX25saW1fbG5kX3N1bSddKQoKWV93YXZlMDFfbmxldmVsMF9peCA8LSB3aGljaChpcy5uYShlbnNfd2F2ZTAxX212JGRhdG1hdFssJ25icF9sbmRfc3VtJ10pKQoKYWxsKGlzLmZpbml0ZShlbnNfd2F2ZTAxX212JGRhdG1hdCkpCgp3aGljaCghaXMuZmluaXRlKGVuc193YXZlMDFfbXYkZGF0bWF0KSwgYXJyLmluZCA9IFRSVUUpCgplbnNfd2F2ZTAxX212JGRhdG1hdFt3aGljaCghaXMuZmluaXRlKGVuc193YXZlMDFfbXYkZGF0bWF0KSwgYXJyLmluZCA9IFRSVUUpXQoKY29sbmFtZXMoZW5zX3dhdmUwMV9tdiRkYXRtYXQpWzldCgoKYGBgCgoKCiMjIFdhdmUwMCBsZXZlbDFhIEVuc2VtYmxlIGJlaGF2aW91ciBpbiBhbGwgb3V0cHV0cwoKCgpOZWVkIHVuaXRzCgoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAxMCwgfQoKCiNwZGYoZmlsZSA9ICdmaWdzL3dhdmUwMC1sZXZlbDFhLW1vZGVybi12YWx1ZS1oaXN0cy5wZGYnLCB3aWR0aCA9IDgsIGhlaWdodCA9IDEwKQpwYXIobWZyb3cgPSBjKDUsMyksIGZnID0gJ2RhcmtncmV5JywgbGFzID0gMSkKZm9yIChpIGluIHlfbmFtZXNfc2VsZWN0KXsKICAKICAKICBoaXN0KFlfbGV2ZWwxYVssIGldLCBtYWluID0gaSwgY29sID0gJ2dyZXknLCB4bGFiID0gc2VsZWN0X3VuaXRzW2ldKQogIGFibGluZSh2ID0gc3RhbmRhcmRfbW9kZXJuX3ZhbHVlW2ldLCBjb2wgPSAnYmxhY2snKQogIAp9CiNkZXYub2ZmKCkKCgoKYGBgCgoKCiMjIFdhdmUwMC9XYXZlMDEgIEVuc2VtYmxlIGJlaGF2aW91ciBpbiBrZXkgKGNvbnN0cmFpbmluZykgb3V0cHV0cy4gCgpHbG9iYWwgbWVhbiBmb3IgdGhlIDIwIHllYXJzIGF0IHRoZSBlbmQgb2YgdGhlIDIwdGggQ2VudHVyeS4gVGhlcmUgaXMgc3RpbGwgYSBzaWduaWZpY2FudCBsb3cgYmlhcyBvbiBjVmVnIG91dHB1dC4KCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOH0Kd2F2ZTAwY29sIDwtICdza3libHVlMicKd2F2ZTAxY29sIDwtICd0b21hdG8yJwoKd2F2ZTAwY29sIDwtICdkb2RnZXJibHVlMicKd2F2ZTAxY29sIDwtICdmaXJlYnJpY2snCnJhbmdlY29sIDwtICdncmV5JwpgYGAKCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDgsIH0KIyBIaXN0b2dyYW0gb2YgbGV2ZWwgMSBjb25zdHJhaW50cwpoY29sID0gJ2RhcmtncmV5JwpsY29sID0gJ2JsYWNrJwoKI3BkZihmaWxlID0gJ2ZpZ3MvbGV2ZWxfMl9jb25zdHJhaW50c19oaXN0cy5wZGYnLCB3aWR0aCA9IDYsIGhlaWdodCA9IDUpCnBhcihtZnJvdyA9IGMoMiwyKSwgZmcgPSAnZGFya2dyZXknLCBsYXMgPSAxLCBvbWEgPSBjKDAuMSwgMC4xLCA0LCAwLjEpKQoKdHJ1bmMgPC0gZnVuY3Rpb24oeCwgdmVjKXsKICAKICBkYXQgPC0geFt4IDwgbWF4KHZlYykgJiB4ID4gbWluKHZlYykgIF0KICAKICBkYXQKICAKfQoKCmggPC0gaGlzdChZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnbmJwX2xuZF9zdW0nXSwgbWFpbiA9ICdOQlAnLCB4bGFiID0gJ0d0Qy95ZWFyJywgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwxNTApKQpoaXN0KHRydW5jKFlfY29uc3Rfd2F2ZTAxX3NjYWxlZCBbLCduYnBfbG5kX3N1bSddLCBoJGJyZWFrcykgLAogICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsMTUwKSAsIGJyZWFrcyA9IGgkYnJlYWtzLCBhZGQgPSBUUlVFKQoKcnVnKFlfY29uc3Rfc3Rhbl9zY2FsZWRbJ25icF9sbmRfc3VtJ10sIGx3ZCA9IDIpCgpwb2x5Z29uKHggPSBjKDAsIDEwMCwgMTAwLCAwKSwgeSA9IGMoMCwgMCwgMTAwMCwgMTAwMCksCiAgICAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHJhbmdlY29sLCA2MCksCiAgICAgICAgYm9yZGVyID0gbWFrZVRyYW5zcGFyZW50KHJhbmdlY29sKSkKCmggPC0gaGlzdChZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnbnBwX25saW1fbG5kX3N1bSddLGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsMTUwKSwgbWFpbiA9ICdOUFAnLCB4bGFiID0gJ0d0Qy95ZWFyJykKaGlzdCh0cnVuYyhZX2NvbnN0X3dhdmUwMV9zY2FsZWQgWywnbnBwX25saW1fbG5kX3N1bSddLCBoJGJyZWFrcykgLCAKICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sKSAsIGJyZWFrcyA9IGgkYnJlYWtzLCBhZGQgPSBUUlVFKQoKcnVnKFlfY29uc3Rfc3Rhbl9zY2FsZWRbJ25wcF9ubGltX2xuZF9zdW0nXSwgbHdkID0gMikKCnBvbHlnb24oeCA9IGMoMzUsIDgwLCA4MCwgMzUpLCB5ID0gYygwLCAwLCAxMDAwLCAxMDAwKSwKICAgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wsIDYwKSwKICAgICAgICBib3JkZXIgPSBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wpKQoKCmggPC0gaGlzdChZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnY1NvaWxfbG5kX3N1bSddLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLDE1MCksIG1haW4gPSAnU29pbCBDYXJib24nLCB4bGFiID0gJ0d0QycpCmhpc3QodHJ1bmMoWV9jb25zdF93YXZlMDFfc2NhbGVkIFssJ2NTb2lsX2xuZF9zdW0nXSwgaCRicmVha3MpICwgCiAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwxNTApICwgYnJlYWtzID0gaCRicmVha3MsIGFkZCA9IFRSVUUpCgpydWcoWV9jb25zdF9zdGFuX3NjYWxlZFsnY1NvaWxfbG5kX3N1bSddLCBsd2QgPSAyKQoKcG9seWdvbih4ID0gYyg3NTAsIDMwMDAsIDMwMDAsIDc1MCksIHkgPSBjKDAsIDAsIDEwMDAsIDEwMDApLAogICAgICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCwgNjApLAogICAgICAgIGJvcmRlciA9IG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCkpCgpoIDwtIGhpc3QoWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLDE1MCksIG1haW4gPSAnVmVnZXRhdGlvbiBDYXJib24nLCB4bGFiID0gJ0d0QycpCmhpc3QodHJ1bmMoWV9jb25zdF93YXZlMDFfc2NhbGVkIFssJ2NWZWdfbG5kX3N1bSddLCBoJGJyZWFrcykgLCAKICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwxNTApICAsIGJyZWFrcyA9IGgkYnJlYWtzLCBhZGQgPSBUUlVFKQoKcnVnKFlfY29uc3Rfc3Rhbl9zY2FsZWRbJ2NWZWdfbG5kX3N1bSddLCBsd2QgPSAyKQoKcG9seWdvbih4ID0gYygzMDAsIDgwMCwgODAwLCAzMDApLCB5ID0gYygwLCAwLCAxMDAwLCAxMDAwKSwKICAgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wsIDYwKSwKICAgICAgIGJvcmRlciA9ICBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wpKQoKI2Rldi5vZmYoKQoKcmVzZXQoKQoKbGVnZW5kKCd0b3AnLCBob3JpeiA9IFRSVUUsIGZpbGwgPSBjKG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDE1MCksIG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDE1MCksIG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCwgNjApKSwgbGVnZW5kID0gYygnV2F2ZTAwJywgJ1dhdmUwMScsICdBVyByYW5nZScpKQpgYGAKIyMgV2hhdCBwcm9wb3J0aW9uIG9mIG1vZGVscyAqbm93KiBmYWxsIHdpdGhpbiBBbmR5J3MgY29uc3RyYWludHM/CgpKdXN0IG92ZXIgYSB0aGlyZCEgQmV0dGVyIHRoYW4gYmVmb3JlLCBidXQgc3RpbGwgbm90IGdyZWF0LiBQb2ludGluZyBhdCBhIHNpZ25pZmljYW50IG1vZGVsIGRpc2NyZXBlbmN5IGluIGNWZWcKCmBgYHtyfQpBV19jb25zdHJhaW50cyA8LSBtYXRyaXgobnJvdyA9IDIsIG5jb2wgPSBsZW5ndGgoeW5hbWVzX2NvbnN0KSkKCkFXX2NvbnN0cmFpbnRzWzEsXSA8LSBjKDAsIDM1LCA3NTAsIDMwMCkKQVdfY29uc3RyYWludHNbMixdIDwtIGMoMTAwLCA4MCwgMzAwMCwgODAwKQoKY29sbmFtZXMoQVdfY29uc3RyYWludHMpIDwtIHluYW1lc19jb25zdApyb3duYW1lcyhBV19jb25zdHJhaW50cykgPC0gYygnbWluJywgJ21heCcpCgoKIyBjb25mb3JtIHRvIEFuZHkncyBiYXNpYyBjb25zdHJhaW50cwojbGV2ZWwyX2l4X3dhdmUwMSA8LSB3aGljaChhcHBseShZX2NvbnN0X3dhdmUwMV9zY2FsZWQsIDEsIEZVTiA9IHdpdGhpblJhbmdlLCBtYXhlcyAgPSBBV19jb25zdHJhaW50c1syLF0sIG1pbnMgPSBBV19jb25zdHJhaW50c1sxLF0gKSkKCiNubGV2ZWwyX2l4X3dhdmUwMSA8LSB3aGljaChhcHBseShZX2NvbnN0X3dhdmUwMV9zY2FsZWQsIDEsIEZVTiA9IHdpdGhpblJhbmdlLCBtYXhlcyAgPSBBV19jb25zdHJhaW50c1syLF0sIG1pbnMgPSBBV19jb25zdHJhaW50c1sxLF0gKSA9PSBGQUxTRSkKCmxldmVsMl9peF93YXZlMDEgPC0gd2hpY2goWV9jb25zdF93YXZlMDFfc2NhbGVkWywnbmJwX2xuZF9zdW0nXSA+IDAgJgogICAgICAgICAgICAgICAgICAgIFlfY29uc3Rfd2F2ZTAxX3NjYWxlZFssJ25wcF9ubGltX2xuZF9zdW0nXSA+IDM1ICYgWV9jb25zdF93YXZlMDFfc2NhbGVkWywnbnBwX25saW1fbG5kX3N1bSddIDwgODAgJgogICAgICAgICAgICAgICAgICAgIFlfY29uc3Rfd2F2ZTAxX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfY29uc3Rfd2F2ZTAxX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSA8IDMwMDAgJgogICAgICAgICAgICAgICAgICBZX2NvbnN0X3dhdmUwMV9zY2FsZWRbLCdjVmVnX2xuZF9zdW0nXSA+IDMwMCAmIFlfY29uc3Rfd2F2ZTAxX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddIDwgODAwCiAgICAgICAgICAgICAgICAgICkKCgpgYGAKCk9mIHRoZSA0MDAgbWVtYmVycyBvZiB0aGUgd2F2ZTAxIGVuc2VtYmxlLCAxMjggcGFzcyBBbmR5IFdpbHRzaGlyZSdzIExldmVsIDIgY29uc3RyYWludHMuCgpgYGB7cn0KCmxlbmd0aChsZXZlbDJfaXhfd2F2ZTAxKQoKYGBgCgpQYWlycyBwbG90IG9mIHRoZSBpbnB1dHMgdGhhdCBwYXNzIHRoZSBjb25zdHJhaW50cyB3aXRoIHJlc3BlY3QgdG8gdGhlIGxpbWl0cyBvZiB0aGUgb3JpZ2luYWwgZW5zZW1ibGUuCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMCB9CgpwYWlycyhYX3dhdmUwMVtsZXZlbDJfaXhfd2F2ZTAxLCBdLAogICAgICB4bGltID0gYygwLDEpLAogICAgICB5bGltID0gYygwLDEpLAogICAgICBnYXAgPSAwLAogICAgICBsb3dlci5wYW5lbCA9IE5VTEwsCiAgICAgIHBjaCA9IDIwLAogICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLDIwMCkKICAgICAgKQoKYGBgCgojIyBUaW1lc2VyaWVzIG9mIG1lYW4gY2FyYm9uIGN5Y2xlIHByb3BlcnRpZXMgb3ZlciB3aG9sZSBydW4uCgpgYGB7cn0KCiMgCiNUaGlzIGhhcyBhIHByb2JsZW0gd2l0aCB2YXJpYWJsZSBsZW5ndGhzIG9mIHRpbWUgaW4gdGhlIHJ1bnMgKGVnIFAwNTA5KQoKIyBtYWtlVGltZXNlcmllc0Vuc2VtYmxlIDwtIGZ1bmN0aW9uKGVuc2xvYywgdmFyaWFibGUsIG5zdGFydCwgbmVuZCwgbnRzID0gMTY0LCBjbiA9IDE4NTA6MjAxMyl7CiMgCiMgICB5c2VjIDwtIDMxNTM2MDAwCiMgCiMgICBuZW5zIDwtIChuZW5kIC0gbnN0YXJ0KSArIDEKIyAgICMgbmVucyBpcyBudW1iZXIgb2YgZW5zZW1ibGUgbWVtYmVycwojICAgIyBudHMgbGVuZ3RoIG9mIHRpbWVzZXJpZXMKIyAgICMgY24gaXMgY29sbmFtZXMoKQojICAgZGF0bWF0IDwtIG1hdHJpeChOQSwgbnJvdyA9IG5lbnMsIG5jb2wgPSBudHMpCiMgICBjb2xuYW1lcyhkYXRtYXQpIDwtIGNuCiMgCiMgICBlbnNsaXN0IDwtIHBhc3RlKCJQIiwgZm9ybWF0Qyhuc3RhcnQ6bmVuZCwgd2lkdGg9NCwgZmxhZz0iMCIpLCBzZXA9IiIpCiMgCiMgICBmb3IoaSBpbiAxOm5lbnMpewojIAojICAgICAjdmVjIDwtIHJlcChOQSxudHMpCiMgCiMgICAgIGVuc21lbWJlciA8LSBlbnNsaXN0W2ldCiMgICAgICNwcmludChlbnNtZW1iZXIpCiMgCiMgICAgIGZuIDwtIHBhc3RlMChlbnNsb2MsJ0pVTEVTLUVTLTFwMF8nLGVuc21lbWJlciwnX0FubnVhbF9nbG9iYWwubmMnKQojIAojIAojICAgICB0cnkobmMgPC0gbmNfb3BlbihwYXN0ZTAoZm4pKSkKIyAgICAgdHJ5KGxvY2FsdGltZSA8LSBuY3Zhcl9nZXQobmMsICd0aW1lJykpCiMgCiMgICAgICMgVGhpcyBwYXJ0IGNvbXBlbnNhdGVzIGZvciB0aGUgZmFjdCB0aGF0IHNvbWV0aW1lcyB5ZWFycyBhcmUgbWlzc2luZwojICAgICB0cnkobG9jYWx5ZWFyIDwtIGZsb29yKDIwMTAgKyAobG9jYWx0aW1lIC8geXNlYykpKQojICAgICB0cnkoaXggPC0gd2hpY2goY24laW4lbG9jYWx5ZWFyKSkKIyAKIyAgICAgdHJ5KGRhdCA8LSBleHRyYWN0VGltZXNlcmllcyhuYywgdmFyaWFibGUpKQojIAojICAgICB0cnkoZGF0bWF0W2ksIGl4XSA8LSBkYXQpCiMgICAgIG5jX2Nsb3NlKG5jKQojICAgfQojICAgZGF0bWF0CiMgfQoKCgojIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUgPC0gZnVuY3Rpb24oZW5zbG9jLCB2YXJpYWJsZSwgbnN0YXJ0LCBuZW5kLCBudHMgPSAxNjQsIGNuID0gMTg1MDoyMDEzKXsKIyAgIAojICAgbmVucyA8LSAobmVuZCAtIG5zdGFydCkgKyAxCiMgICAjIG5lbnMgaXMgbnVtYmVyIG9mIGVuc2VtYmxlIG1lbWJlcnMKIyAgICMgbnRzIGxlbmd0aCBvZiB0aW1lc2VyaWVzCiMgICAjIGNuIGlzIGNvbG5hbWVzKCkKIyAgIGRhdG1hdCA8LSBtYXRyaXgoTkEsIG5yb3cgPSBuZW5zLCBuY29sID0gbnRzKQojICAgY29sbmFtZXMoZGF0bWF0KSA8LSBjbgojICAgCiMgICBlbnNsaXN0IDwtIHBhc3RlKCJQIiwgZm9ybWF0Qyhuc3RhcnQ6bmVuZCwgd2lkdGg9NCwgZmxhZz0iMCIpLCBzZXA9IiIpCiMgICAjZmxvYyA8LSBwYXN0ZTAoZW5zbG9jLGVuc21lbWJlcixzdWJkaXIpCiMgICAKIyAgIGZvcihpIGluIDE6bmVucyl7CiMgICAgIAojICAgICB2ZWMgPC0gcmVwKE5BLG50cykKIyAgICAgCiMgICAgIGVuc21lbWJlciA8LSBlbnNsaXN0W2ldIAojICAgICAKIyAgICAgZm4gPC0gcGFzdGUwKGVuc2xvYyxlbnNtZW1iZXIsJy9zdGF0cy8nLCdKVUxFUy1FUy0xcDBfJyxlbnNtZW1iZXIsJ19Bbm51YWxfZ2xvYmFsLm5jJykKIyAgICAgCiMgICAgIAojICAgICB0cnkobmMgPC0gbmNfb3BlbihwYXN0ZTAoZm4pKSkKIyAgICAgdHJ5KGRhdCA8LSBleHRyYWN0VGltZXNlcmllcyhuYywgdmFyaWFibGUpKQojICAgICAKIyAgICAgZGF0bWF0W2ksIF0gPC0gZGF0CiMgICAgIG5jX2Nsb3NlKG5jKQojICAgfQojICAgZGF0bWF0CiMgfQoKYGBgCgoKYGBge3J9CgppZiAoZmlsZS5leGlzdHMoImVuc2VtYmxlX3RpbWVzZXJpZXNfd2F2ZTAxXzIwMjItMDQtMDgucmRhdGEiKSkgewogIGxvYWQoImVuc2VtYmxlX3RpbWVzZXJpZXNfd2F2ZTAxXzIwMjItMDQtMDgucmRhdGEiKQp9IGVsc2UgewogIAogICMgcHJpbWFyeSBjYXJib24gY3ljbGUgb3V0cHV0cwogICBucHBfZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvY193YXZlMDEsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCwgdmFyaWFibGUgPSAibnBwX25saW1fbG5kX3N1bSIpIC8gKDFlMTIveXNlYykKICAgbmJwX2Vuc193YXZlMDEgPC0gIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jX3dhdmUwMSxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLHZhcmlhYmxlID0gIm5icF9sbmRfc3VtIikgLyAoMWUxMi95c2VjKQogICBjU29pbF9lbnNfd2F2ZTAxIDwtICBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvY193YXZlMDEsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCx2YXJpYWJsZSA9ICJjU29pbF9sbmRfc3VtIikgLyAxZTEyCiAgIGNWZWdfZW5zX3dhdmUwMSA8LSAgbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2Nfd2F2ZTAxLG5zdGFydCA9IG5zdGFydCwgbmVuZCA9IG5lbmQsdmFyaWFibGUgPSAiY1ZlZ19sbmRfc3VtIikgLyAxZTEyCiAgIyAKICAjIAogICBsYWlfbG5kX21lYW5fZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvY193YXZlMDEsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCx2YXJpYWJsZSA9ICJsYWlfbG5kX21lYW4iKQogICMgCiAgIyAjIGZsdXhlcwogICByaF9sbmRfc3VtX2Vuc193YXZlMDEgPC0gbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2Nfd2F2ZTAxLG5zdGFydCA9IG5zdGFydCwgbmVuZCA9IG5lbmQsIHZhcmlhYmxlID0gInJoX2xuZF9zdW0iKSAvICgxZTEyL3lzZWMpCiAgIGZMdWNfbG5kX3N1bV9lbnNfd2F2ZTAxIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jX3dhdmUwMSxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLCB2YXJpYWJsZSA9ICJmTHVjX2xuZF9zdW0iKSAvICgxZTEyL3lzZWMpCiAgIGZIYXJ2ZXN0X2xuZF9zdW1fZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvY193YXZlMDEsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCwgdmFyaWFibGUgPSAiZkhhcnZlc3RfbG5kX3N1bSIpIC8gKDFlMTIveXNlYykKICAjIAogICMgCiAgIyAjIGZyYWN0aW9ucwogICB0cmVlRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jX3dhdmUwMSxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLCAgdmFyaWFibGUgPSAidHJlZUZyYWNfbG5kX21lYW4iKQogICBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvY193YXZlMDEsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCwgIHZhcmlhYmxlID0gInNocnViRnJhY19sbmRfbWVhbiIpCiAgIGJhcmVzb2lsRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jX3dhdmUwMSxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLCAgdmFyaWFibGUgPSAiYmFyZXNvaWxGcmFjX2xuZF9tZWFuIikKICAgCiAgICAgYzNQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEgPC0gbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2Nfd2F2ZTAxLCBuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLHZhcmlhYmxlID0gImMzUGZ0RnJhY19sbmRfbWVhbiIpCiAgYzRQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEgPC0gbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2Nfd2F2ZTAxLCBuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLHZhcmlhYmxlID0gImM0UGZ0RnJhY19sbmRfbWVhbiIpCiAgIAogICAKICAKICAgc2F2ZShucHBfZW5zX3dhdmUwMSwKICAgICAgICBuYnBfZW5zX3dhdmUwMSwKICAgICAgICBjU29pbF9lbnNfd2F2ZTAxLAogICAgICAgIGNWZWdfZW5zX3dhdmUwMSwKICAgICAgICBsYWlfbG5kX21lYW5fZW5zX3dhdmUwMSwKICAgICAgICByaF9sbmRfc3VtX2Vuc193YXZlMDEsCiAgICAgICAgZkx1Y19sbmRfc3VtX2Vuc193YXZlMDEsCiAgICAgICAgZkhhcnZlc3RfbG5kX3N1bV9lbnNfd2F2ZTAxLAogICAgICAgIHRyZWVGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsCiAgICAgICAgc2hydWJGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsCiAgICAgICAgYmFyZXNvaWxGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsCiAgICAgICAgYzNQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsCiAgICAgICAgYzRQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsCiAgICAgICBmaWxlID0gImVuc2VtYmxlX3RpbWVzZXJpZXNfd2F2ZTAxXzIwMjItMDQtMDgucmRhdGEiICkKfQoKI3RvdGFsX2xhbmRfY2FyYm9uX2Vuc193YXZlMDEgPC0gY1NvaWxfZW5zX3dhdmUwMSArIGNWZWdfZW5zX3dhdmUwMQoKYGBgCgoKCiMjIHRpbWVzZXJpZXMgb3V0bGllcnMKClRpbWVzZXJpZXMgdGhhdCBoYXZlIHByb2JsZW1zLiBOQlAsIFJIIGFuZCBjU29pbCBzZWVtcyB0byBoYXZlIGxhcmdlIG91dGxpZXJzCmBgYHtyfQoKIyBUaGVzZSBpbmRpY2VzIHJlZmVyZW5jZSB0aGUgc2VwYXJhdGUgZW5zZW1ibGVzCiMgY1NvaWwgb3ZlciA2MDAwCiMgcmggb3ZlciAyMDAKIyBuYnAgbGVzcyB0aGFuIC0xNQpjU29pbF9vdXRsaWVyX2l4X3dhdmUwMCA8LSB1bmlxdWUod2hpY2goY1NvaWxfZW5zID4gNjAwMCwgYXJyLmluZCA9IFRSVUUpWywncm93J10pCmNTb2lsX291dGxpZXJfaXhfd2F2ZTAxIDwtIHVuaXF1ZSh3aGljaChjU29pbF9lbnNfd2F2ZTAxID4gNjAwMCwgYXJyLmluZCA9IFRSVUUpWywncm93J10pCgpuYnBfb3V0bGllcl9peF93YXZlMDAgPC0gdW5pcXVlKHdoaWNoKG5icF9lbnMgPCAtMTUsIGFyci5pbmQgPSBUUlVFKVssJ3JvdyddKQpuYnBfb3V0bGllcl9peF93YXZlMDEgPC0gdW5pcXVlKHdoaWNoKG5icF9lbnNfd2F2ZTAxIDwgLTE1LCBhcnIuaW5kID0gVFJVRSlbLCdyb3cnXSkKCnJoX2xuZF9zdW1fb3V0bGllcl9peF93YXZlMDAgPC0gdW5pcXVlKHdoaWNoKHJoX2xuZF9zdW1fZW5zID4gMjAwLCBhcnIuaW5kID0gVFJVRSlbLCdyb3cnXSkKcmhfbG5kX3N1bV9vdXRsaWVyX2l4X3dhdmUwMSA8LSB1bmlxdWUod2hpY2gocmhfbG5kX3N1bV9lbnNfd2F2ZTAxID4gMjAwLCBhcnIuaW5kID0gVFJVRSlbLCdyb3cnXSkKCgojIGFyZSB0aGVyZSBhZGRpdGlvbmFsIGV4Y2x1ZGVkIGluZGljZXMgdG8gdGhvc2UgYWxyZWFkeSBleGNsdWRlZCBieSB0aGUgY29uc3RyYWludAoKd2F2ZTAxX2FsbF9peCA8LSAxOm50cmFpbl93YXZlMDEKCiMgSW5kaWNlcyBleGNsdWRlZCBpbiB3YXZlMDEgbGV2ZWwyCmxldmVsMl9uaXhfd2F2ZTAxIDwtIHNldGRpZmYod2F2ZTAxX2FsbF9peCwgbGV2ZWwyX2l4X3dhdmUwMSkKCiMgd291bGQgYmUgaW50ZXJlc3RpbmcgdG8gc2VlIGlmIHRoZXNlIGxvb2sgbm9ybWFsIGluIG90aGVyIHdheXMKdHNfb3V0bGllcnNfaXhfd2F2ZTAxIDwtIHVuaXF1ZShjKGNTb2lsX291dGxpZXJfaXhfd2F2ZTAxLG5icF9vdXRsaWVyX2l4X3dhdmUwMSwgcmhfbG5kX3N1bV9vdXRsaWVyX2l4X3dhdmUwMSkpCiMgYXJlIHRoZXJlIGFueSB0aGF0IGFyZSBub3QgZXhjbHVkZWQgYnkgbGV2ZWwgMj8gKEkgYXNzdW1lIHNvKQppbnRlcnNlY3QodHNfb3V0bGllcnNfaXhfd2F2ZTAxLCBsZXZlbDJfbml4X3dhdmUwMSkKCndpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxIDwtIHNldGRpZmYod2F2ZTAxX2FsbF9peCx0c19vdXRsaWVyc19peF93YXZlMDEpCgojIFJlbW92ZSB0aGVzZSBmcm9tIHRoZSB3YXZlMDEgZW5zZW1ibGUgdG8gcmVtb3ZlIG91dGxpZXJzIGFuZCBleGNsdWRlZCBlbnNlbWJsZSBtZW1iZXJzCmxldmVsMl9hbmRfdHNfb3V0bGllcnNfbml4X3dhdmUwMSA8LSB1bmlvbihsZXZlbDJfbml4X3dhdmUwMSwgdHNfb3V0bGllcnNfaXhfd2F2ZTAxKQoKbGV2ZWwyYV9peF93YXZlMDEgPC0gc2V0ZGlmZih3YXZlMDFfYWxsX2l4LCBsZXZlbDJfYW5kX3RzX291dGxpZXJzX25peF93YXZlMDEpCgp3YXZlMDBfYWxsX2l4IDwtIDE6NDk5CnRzX291dGxpZXJzX2l4X3dhdmUwMCA8LSB1bmlxdWUoYyhjU29pbF9vdXRsaWVyX2l4X3dhdmUwMCxuYnBfb3V0bGllcl9peF93YXZlMDAsIHJoX2xuZF9zdW1fb3V0bGllcl9peF93YXZlMDApKQp3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCA8LSBzZXRkaWZmKHdhdmUwMF9hbGxfaXgsdHNfb3V0bGllcnNfaXhfd2F2ZTAwKQoKYGBgCgoKCmBgYHtyIHBsb3QtY2FyYm9uLWN5Y2xlLXRpbWVzZXJpZXMtcHJpbWFyeSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMiwgfQoKbGNvbF93YXZlMCA8LSBtYWtlVHJhbnNwYXJlbnQoJ2RvZGdlcmJsdWUyJywgIDEyMCkKbGNvbF93YXZlMDEgPC0gbWFrZVRyYW5zcGFyZW50KCdmaXJlYnJpY2snLCAgMTIwKQpsY29sX3dhdmUwMV9sZXZlbDIgPC0gJ2dvbGQnCnN0YW5jb2wgPSAnYmxhY2snCgpsaW5lUGxvdE11bHRpRW5zIDwtIGZ1bmN0aW9uKHllYXJzLCBlbnMxLCBlbnMyLCBlbnMzLCBjb2wxLCBjb2wyLCBjb2wzLCB5bGFiLCBtYWluLCB5bGltID0gTlVMTCl7CiAgIyBQbG90IHdhdmUwMCBhbmQgd2F2ZTAxIHRpbWVzZXJpZXMgb24gdG9wIG9mIG9uZSBhbm90aGVyCiAgCiAgbnQgPC0gbGVuZ3RoKHllYXJzKSAKICBpZihpcy5udWxsKHlsaW0pKXsKICAgIAogIHlsaW0gPSByYW5nZShjKGVuczFbLDFdLCBlbnMxWyxudF0sIGVuczJbLDFdLCBlbnMyWyAsbnRdLCBlbnMzWywxXSwgZW5zM1ssIG50XSkpCiAgfQogIAogIGVsc2UgeWxpbSA8LSB5bGltCiAgCiAgbWF0cGxvdCh5ZWFycywgdChlbnMxKSwgdHlwZSA9ICdsJywgbHR5ID0gJ3NvbGlkJyx5bGltID0geWxpbSwgY29sID0gY29sMSwKICAgICAgICB5bGFiID0geWxhYiwgbWFpbiA9IG1haW4sIHhsYWIgPSAnJywKICAgICAgICBidHkgPSAnbicpCiAgbWF0bGluZXMoeWVhcnMsIHQoZW5zMiksIGNvbCA9IGNvbDIsIGx0eSA9ICdzb2xpZCcpCiAgICBtYXRsaW5lcyh5ZWFycywgdChlbnMzKSwgY29sID0gY29sMywgbHR5ID0gJ3NvbGlkJykKfQoKI3BkZihmaWxlID0gJ2ZpZ3MvY2FyYm9uLWN5Y2xlLXRpbWVzZXJpZXMtd2F2ZXMtY29uc3RyYWluZWQucGRmJywgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTIpCnBhcihtZnJvdz0gYygzLDUpLCBsYXMgPSAxLCBtYXIgPSBjKDQsNCwxLDApKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gbnBwX2Vuc1t3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBucHBfZW5zX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSBucHBfZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdOUFAnKQoKbGluZXMoeWVhcnMsbnBwX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSAgbmJwX2Vuc1t3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLCAKICAgICAgICAgICAgICAgICBlbnMyID0gbmJwX2Vuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gbmJwX2Vuc193YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSxjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdOQlAnLCB5bGltID0gYygtMTAsMTApKQoKbGluZXMoeWVhcnMsIG5icF9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gY1NvaWxfZW5zW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IGNTb2lsX2Vuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gY1NvaWxfZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdjU29pbCcsIHlsaW0gPSByYW5nZShjKGNTb2lsX2Vuc1ssMV0sIGNTb2lsX2Vuc1ssMTY0XSkpKQoKbGluZXMoeWVhcnMsIGNTb2lsX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBjVmVnX2Vuc1t3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBjVmVnX2Vuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gY1ZlZ19lbnNfd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2NWZWcnKQoKbGluZXMoeWVhcnMsIGNWZWdfc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGxhaV9sbmRfbWVhbl9lbnNbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gbGFpX2xuZF9tZWFuX2Vuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gbGFpX2xuZF9tZWFuX2Vuc193YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnTGFpJykKCmxpbmVzKHllYXJzLCBsYWlfbG5kX21lYW5fc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IHJoX2xuZF9zdW1fZW5zW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IHJoX2xuZF9zdW1fZW5zX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSByaF9sbmRfc3VtX2Vuc193YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ1JIJykKCmxpbmVzKHllYXJzLCByaF9sbmRfc3VtX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBmTHVjX2xuZF9zdW1fZW5zW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IGZMdWNfbG5kX3N1bV9lbnNfd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGZMdWNfbG5kX3N1bV9lbnNfd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2ZMdWMnKQoKbGluZXMoeWVhcnMsIGZMdWNfbG5kX3N1bV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gZkhhcnZlc3RfbG5kX3N1bV9lbnNbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gZkhhcnZlc3RfbG5kX3N1bV9lbnNfd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGZIYXJ2ZXN0X2xuZF9zdW1fZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdmSGFydmVzdCcpCgpsaW5lcyh5ZWFycywgZkhhcnZlc3RfbG5kX3N1bV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gdHJlZUZyYWNfbG5kX21lYW5fZW5zW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IHRyZWVGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gdHJlZUZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnJScsIG1haW4gPSAndHJlZWZyYWMnCiAgICAgICAgICAgICAgICAgKQoKbGluZXMoeWVhcnMsIHRyZWVGcmFjX2xuZF9tZWFuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IHNocnViRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IHNocnViRnJhY19sbmRfbWVhbl9lbnNbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJyUnLCBtYWluID0gJ3NocnViZnJhYycKKQoKbGluZXMoeWVhcnMsIHNocnViRnJhY19sbmRfbWVhbl9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gYmFyZXNvaWxGcmFjX2xuZF9tZWFuX2Vuc1t3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnJScsIG1haW4gPSAnYmFyZXNvaWxmcmFjJykKCmxpbmVzKHllYXJzLCBiYXJlc29pbEZyYWNfbG5kX21lYW5fc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGMzUGZ0RnJhY19sbmRfbWVhbl9lbnNbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gYzNQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gYzNQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJyUnLCBtYWluID0gJ2MzUGZ0RnJhYycpCgpsaW5lcyh5ZWFycywgYzNQZnRGcmFjX2xuZF9tZWFuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBjNFBmdEZyYWNfbG5kX21lYW5fZW5zW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sCiAgICAgICAgICAgICAgICAgZW5zMiA9IGM0UGZ0RnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGM0UGZ0RnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICclJywgbWFpbiA9ICdjM1BmdEZyYWMnKQoKbGluZXMoeWVhcnMsIGM0UGZ0RnJhY19sbmRfbWVhbl9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKCnJlc2V0KCkKCmxlZ2VuZCgnYm90dG9tcmlnaHQnLCBsZWdlbmQgPSBjKCd3YXZlMDAnLCd3YXZlMDEnLCd3YXZlMDEgbGV2ZWwyJywnc3RhbmRhcmQnKSwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMS41LCBjb2wgPSBjKGxjb2xfd2F2ZTAsIGxjb2xfd2F2ZTAxLCBsY29sX3dhdmUwMV9sZXZlbDIsIHN0YW5jb2wpLCBpbnNldCA9IGMoMC4wNSwgMC4xNSkgKQoKI2Rldi5vZmYoKQpgYGAKVGhpcyBpcyBhIHBsb3Qgb2YgdGltZXNlcmllcyBvZiBXYXZlMDAsIFdhdmUwMSwgYW5kIGxldmVsMi1jb25zdHJhaW5lZCB3YXZlMDEgb24gdG9wIG9mIG9uZSBhbm90aGVyLiBXZSBzZWUgdGhhdCB0aGUgd2F2ZTAxIGlzIGNsb3NlciB0byB0aGUgc3RhbmRhcmQgdGhhbiB3YXZlMDAsIGFuZCB0aGUgbGV2ZWwtMiBjb25zdHJhaW5lZCB3YXZlMDEgZW5zZW1ibGUgaXMgb2Z0ZW4gY2xvc2VyIGFnYWluLiBIb3dldmVyLCB0aGVyZSBhcmUgc3RpbGwgcXVpdGUgbGFyZ2UgZGlzY3JlcGFuY2llcy4gRm9yIGV4YW1wbGUsIGJhcmVzb2lsZnJhYyBpcyBvZnRlbiB3YXkgdG9vIGhpZ2gsIHNocnViZnJhYyBpcyBvZnRlbiB0b28gbG93ICh0aG91Z2ggYm90aCB0aGVzZSBzcGFuIHRoZSBzdGFuZGFyZCkuIFRyZWVmcmFjIGlzIGF3YXkgZnJvbSB6ZXJvLCBidXQgc3RpbGwgb2Z0ZW4gdG9vIGxvdyBvciB0b28gaGlnaC4gV2hpbGUgZkhhcnZlc3QgbG9va3MgZ29vZCwgZkx1YyBkb2VzIG5vdCBhcHBlYXIgY29uc3RyYWluZWQgYnkgdGhlIHByb2Nlc3MgYXQgYWxsLiBSSCAoc29pbCByZXNwaXJhdGlvbikgbG9va3Mgd2VsbCBjb25zdHJhaW5lZCwgd2hlcmVhcyBsYWkgaXMgb2Z0ZW4gdG9vIGxvdy4gIAoKT25lIHRoaW5nIHdlIGNvdWxkIGRvIG5leHQgaXMgY29uc3RyYWluIGlucHV0IHNwYWNlIGFnYWluLCB1c2luZyBvYnNlcnZhdGlvbnMgb3IgInRvbGVyYW5jZSB0byBlcnJvciIgb24gc29tZSBvciBhbGwgb2YgdGhlc2Ugb3V0cHV0cy4gIAoKV2UgY291bGQgYWxzbyBleHRlbmQgc2Vuc2l0aXZpdHkgYW5hbHlzaXMgdG8gd29yayBvdXQgd2hhdCBjb250cm9scyBlLmcuIHRyZWVmcmFjLgoKQSBzdW1tYXJ5IG9mIHdoYXQgZ2V0cyBjb25zdHJhaW5lZCB3aGVuIHlvdSBjb25zdHJhaW4gdGhlIHRvcC1sZXZlbCB2YXJpYWJsZXMgd291bGQgYmUgYSB1c2VmdWwgYWFuYWx5c2lzLgoKCiMjIEFub21hbHkgdGltZXNlcmllcwoKU2ltaWxhciB0byBhYm92ZSwgYnV0IG91dHB1dCBhbm9tYWx5CmBgYHtyfQpucHBfZW5zX2Fub21fd2F2ZTAxIDwtIGFub21hbGl6ZVRTbWF0cml4KG5wcF9lbnNfd2F2ZTAxLCAxOjIwKQpuYnBfZW5zX2Fub21fd2F2ZTAxIDwtIGFub21hbGl6ZVRTbWF0cml4KG5icF9lbnNfd2F2ZTAxLCAxOjIwKQpjU29pbF9lbnNfYW5vbV93YXZlMDEgPC0gYW5vbWFsaXplVFNtYXRyaXgoY1NvaWxfZW5zX3dhdmUwMSwgMToyMCkKY1ZlZ19lbnNfYW5vbV93YXZlMDEgPC0gYW5vbWFsaXplVFNtYXRyaXgoY1ZlZ19lbnNfd2F2ZTAxLCAxOjIwKQoKcmhfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDEgPC0gYW5vbWFsaXplVFNtYXRyaXgocmhfbG5kX3N1bV9lbnNfd2F2ZTAxLCAxOjIwKQpmTHVjX2xuZF9zdW1fZW5zX2Fub21fd2F2ZTAxIDwtIGFub21hbGl6ZVRTbWF0cml4KGZMdWNfbG5kX3N1bV9lbnNfd2F2ZTAxLCAxOjIwKQpsYWlfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxIDwtIGFub21hbGl6ZVRTbWF0cml4KGxhaV9sbmRfbWVhbl9lbnNfd2F2ZTAxLCAxOjIwKSAKCmZIYXJ2ZXN0X2xuZF9zdW1fZW5zX2Fub21fd2F2ZTAxIDwtIGFub21hbGl6ZVRTbWF0cml4KGZIYXJ2ZXN0X2xuZF9zdW1fZW5zX3dhdmUwMSwgMToyMCkKdHJlZUZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxIDwtIGFub21hbGl6ZVRTbWF0cml4KHRyZWVGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsIDE6MjApCnNocnViRnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDEgPC0gYW5vbWFsaXplVFNtYXRyaXgoc2hydWJGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsIDE6MjApCmJhcmVzb2lsRnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDEgPC0gYW5vbWFsaXplVFNtYXRyaXgoYmFyZXNvaWxGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsIDE6MjApCmMzUGZ0RnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDEgPC0gYW5vbWFsaXplVFNtYXRyaXgoYzNQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsIDE6MjApCmM0UGZ0RnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDEgPC0gYW5vbWFsaXplVFNtYXRyaXgoYzRQZnRGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsIDE6MjApCgojdG90YWxfbGFuZF9jYXJib25fYW5vbV93YXZlMDEgPC0gYW5vbWFsaXplVFNtYXRyaXgodG90YWxfbGFuZF9jYXJib25fZW5zX3dhdmUwMSwgMToyMCkKCgoKCgpgYGAKCmBgYHtyfQoKYW5vbWFsaXplVFMgPC0gZnVuY3Rpb24oeCwgaXggPSAxOjIwKXt4IC0gbWVhbih4W2l4XSkgfSAKbnBwX3N0YW5fYW5vbSA8LSBhbm9tYWxpemVUUyhucHBfc3RhbikKCm5icF9zdGFuX2Fub20gPC0gYW5vbWFsaXplVFMobmJwX3N0YW4pCmNTb2lsX3N0YW5fYW5vbSA8LWFub21hbGl6ZVRTKGNTb2lsX3N0YW4pCmNWZWdfc3Rhbl9hbm9tIDwtIGFub21hbGl6ZVRTKGNWZWdfc3RhbikKbGFpX2xuZF9tZWFuX3N0YW5fYW5vbSA8LSBhbm9tYWxpemVUUyhsYWlfbG5kX21lYW5fc3RhbikKCgojIGZsdXhlcwpyaF9sbmRfc3VtX3N0YW5fYW5vbSA8LSBhbm9tYWxpemVUUyhyaF9sbmRfc3VtX3N0YW4pCmZMdWNfbG5kX3N1bV9zdGFuX2Fub20gPC0gYW5vbWFsaXplVFMoZkx1Y19sbmRfc3VtX3N0YW4pCmZIYXJ2ZXN0X2xuZF9zdW1fc3Rhbl9hbm9tIDwtIGFub21hbGl6ZVRTKGZIYXJ2ZXN0X2xuZF9zdW1fc3RhbikKCgojIGZyYWN0aW9ucwp0cmVlRnJhY19sbmRfbWVhbl9zdGFuX2Fub20gPC0gYW5vbWFsaXplVFModHJlZUZyYWNfbG5kX21lYW5fc3RhbikKc2hydWJGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSA8LSBhbm9tYWxpemVUUyhzaHJ1YkZyYWNfbG5kX21lYW5fc3RhbikKYmFyZXNvaWxGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSA8LSBhbm9tYWxpemVUUyhiYXJlc29pbEZyYWNfbG5kX21lYW5fc3RhbikKYzNQZnRGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSA8LSBhbm9tYWxpemVUUyhjM1BmdEZyYWNfbG5kX21lYW5fc3RhbikKYzRQZnRGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSA8LSBhbm9tYWxpemVUUyhjNFBmdEZyYWNfbG5kX21lYW5fc3RhbikKYGBgCgoKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEyLCB9CgoKI3BkZihmaWxlID0gJ2ZpZ3MvY2FyYm9uLWN5Y2xlLXRpbWVzZXJpZXMtYW5vbWFseS13YXZlcy1jb25zdHJhaW5lZC5wZGYnLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMikKcGFyKG1mcm93PSBjKDMsNSksIGxhcyA9IDEsIG1hciA9IGMoNCw0LDEsMCkpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBucHBfZW5zX2Fub21bd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gbnBwX2Vuc19hbm9tX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSBucHBfZW5zX2Fub21fd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ05QUCcpCgpsaW5lcyh5ZWFycyxucHBfc3Rhbl9hbm9tLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSAgbmJwX2Vuc19hbm9tW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLF0sIAogICAgICAgICAgICAgICAgIGVuczIgPSBuYnBfZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IG5icF9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSxjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdOQlAnLCB5bGltID0gYygtMTAsMTApKQoKbGluZXMoeWVhcnMsIG5icF9zdGFuX2Fub20sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBjU29pbF9lbnNfYW5vbVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBjU29pbF9lbnNfYW5vbV93YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gY1NvaWxfZW5zX2Fub21fd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2NTb2lsJywgeWxpbSA9IHJhbmdlKGMoY1NvaWxfZW5zX2Fub21bLDFdLCBjU29pbF9lbnNfYW5vbVssMTY0XSkpKQoKbGluZXMoeWVhcnMsIGNTb2lsX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGNWZWdfZW5zX2Fub21bd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gY1ZlZ19lbnNfYW5vbV93YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gY1ZlZ19lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnY1ZlZycpCgpsaW5lcyh5ZWFycywgY1ZlZ19zdGFuX2Fub20sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBsYWlfbG5kX21lYW5fZW5zX2Fub21bd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gbGFpX2xuZF9tZWFuX2Vuc19hbm9tX3dhdmUwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdLAogICAgICAgICAgICAgICAgIGVuczMgPSBsYWlfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxW2xldmVsMmFfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ0xhaScpCgpsaW5lcyh5ZWFycywgbGFpX2xuZF9tZWFuX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IHJoX2xuZF9zdW1fZW5zX2Fub21bd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gcmhfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gcmhfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ1JIJykKCmxpbmVzKHllYXJzLCByaF9sbmRfc3VtX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGZMdWNfbG5kX3N1bV9lbnNfYW5vbVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBmTHVjX2xuZF9zdW1fZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGZMdWNfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnZkx1YycpCgpsaW5lcyh5ZWFycywgZkx1Y19sbmRfc3VtX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGZIYXJ2ZXN0X2xuZF9zdW1fZW5zX2Fub21bd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gZkhhcnZlc3RfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDFbd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDEsXSwKICAgICAgICAgICAgICAgICBlbnMzID0gZkhhcnZlc3RfbG5kX3N1bV9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnZkhhcnZlc3QnKQoKbGluZXMoeWVhcnMsIGZIYXJ2ZXN0X2xuZF9zdW1fc3Rhbl9hbm9tLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gdHJlZUZyYWNfbG5kX21lYW5fZW5zX2Fub21bd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSwKICAgICAgICAgICAgICAgICBlbnMyID0gdHJlZUZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IHRyZWVGcmFjX2xuZF9tZWFuX2Vuc19hbm9tX3dhdmUwMVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnJScsIG1haW4gPSAndHJlZWZyYWMnCiAgICAgICAgICAgICAgICAgKQoKbGluZXMoeWVhcnMsIHRyZWVGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IHNocnViRnJhY19sbmRfbWVhbl9lbnNfYW5vbVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IHNocnViRnJhY19sbmRfbWVhbl9lbnNfYW5vbVtsZXZlbDJhX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnJScsIG1haW4gPSAnc2hydWJmcmFjJwopCgpsaW5lcyh5ZWFycywgc2hydWJGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGJhcmVzb2lsRnJhY19sbmRfbWVhbl9lbnNfYW5vbVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGJhcmVzb2lsRnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJyUnLCBtYWluID0gJ2JhcmVzb2lsZnJhYycpCgpsaW5lcyh5ZWFycywgYmFyZXNvaWxGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGMzUGZ0RnJhY19sbmRfbWVhbl9lbnNfYW5vbVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBjM1BmdEZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGMzUGZ0RnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJyUnLCBtYWluID0gJ2MzUGZ0RnJhYycpCgpsaW5lcyh5ZWFycywgYzNQZnRGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGM0UGZ0RnJhY19sbmRfbWVhbl9lbnNfYW5vbVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMCxdLAogICAgICAgICAgICAgICAgIGVuczIgPSBjNFBmdEZyYWNfbG5kX21lYW5fZW5zX2Fub21fd2F2ZTAxW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLF0sCiAgICAgICAgICAgICAgICAgZW5zMyA9IGM0UGZ0RnJhY19sbmRfbWVhbl9lbnNfYW5vbV93YXZlMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJyUnLCBtYWluID0gJ2MzUGZ0RnJhYycpCgpsaW5lcyh5ZWFycywgYzRQZnRGcmFjX2xuZF9tZWFuX3N0YW5fYW5vbSwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCnJlc2V0KCkKCmxlZ2VuZCgnYm90dG9tcmlnaHQnLCBsZWdlbmQgPSBjKCd3YXZlMDAnLCd3YXZlMDEnLCd3YXZlMDEgbGV2ZWwyJywnc3RhbmRhcmQnKSwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMS41LCBjb2wgPSBjKGxjb2xfd2F2ZTAsIGxjb2xfd2F2ZTAxLCBsY29sX3dhdmUwMV9sZXZlbDIsIHN0YW5jb2wpLCBpbnNldCA9IGMoMC4wNSwgMC4xNSkgKQoKCiNkZXYub2ZmKCkKCmBgYAoKCgoKIyBFbXVsYXRvciBmaXRzCgpXZSBob3BlIHRoYXQgcnVubmluZyB0aGUgbmV3IGVuc2VtYmxlIGdpdmVzIHVzIGEgYmV0dGVyIGVtdWxhdG9yLCBhbmQgYWxsb3dzIHVzIHRvIHJ1bGUgb3V0IG1vcmUgaW5wdXQgc3BhY2UuCldlIHBhcnRpY3VsYXJseSBob3BlIHRoYXQgdGhlIGVtdWxhdG9yIGlzIGJldHRlciBmb3IgdGhvc2UgbWVtYmVycyB0aGF0IGFyZSBpbnNpZGUgQVcncyBjb25zdHJhaW50cy4KCkZpcnN0LCB3ZSBjYW4gbG9vayBhdCB0aGUgZW11bGF0b3IgZXJyb3JzIGluIHR3byBjYXNlczogVGhlIGxldmVsMWEgZGF0YSAoYSBiYXNpYyBjYXJib24gY3ljbGUpLCBhbmQgdGhlbiB3aXRoIHRoZSBXYXZlMDEgZGF0YSwgd2hpY2ggc2hvdWxkIGhhdmUgc2ltaWxhciBjaGFyYWN0ZXJpc3RpY3MuIChXZSBzaG91bGQgaGF2ZSBlbGltaW5hdGVkIHJlYWxseSBiYWQgc2ltdWxhdGlvbnMsIGJ1dCB3YXZlMDEgaXMgbm90IGNvbnN0cmFpbmVkIHRoZSBkYXRhIHBlcmZlY3RseSB0byBiZSB3aXRoaW4gQVcgY29uc3RyYWludHMuKQoKCmBgYHtyLCB9CiMjIEVtdWxhdG9yIGZpdCBsaXN0IG9mIGxldmVsIDFhIGVuc2VtYmxlCgojZml0X2xpc3RfY29uc3RfbGV2ZWwxYSA8LSBjcmVhdGVLbUZpdExpc3QoWCA9IFhfbGV2ZWwxYSwgWSA9IFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpCgpZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2xpc3QgPC0gbWF0Mmxpc3QoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkKCmZpdF9saXN0X2NvbnN0X2xldmVsMWEgPC0gbWNsYXBwbHkoWCA9IFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRfbGlzdCwgRlVOID0ga20sIGZvcm11bGEgPSB+LiwgZGVzaWduID0gWF9sZXZlbDFhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1jLmNvcmVzID0gNCwgY29udHJvbCA9IGxpc3QodHJhY2UgPSBGQUxTRSkpCgoKCgpgYGAKCgoKYGBge3IsIH0KIyMgUmVtb3ZlIGFueSAgdGltZXNlcmllcyBvdXRsaWVycyBmcm9tIHRoZSBuZXcgd2F2ZSBhbmQgYnVpbGQgZW11bGF0b3JzCiMgQmluZCB0b2dldGhlciB0aGUgaW5wdXQgbWF0cmljZXMgYW5kIHNjYWxlZCBvdXRwdXQgZGF0YQoKCiNYX2xldmVsMWFfd2F2ZTAxIDwtIHJiaW5kKFhfbGV2ZWwxYSwgWF93YXZlMDFfdHJhaW4pWy00NDAsIF0KWF9sZXZlbDFhX3dhdmUwMSA8LSByYmluZChYX2xldmVsMWEsIFhfd2F2ZTAxX3RyYWluW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLCBdKQoKWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQgPC0gcmJpbmQoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCwgWV9jb25zdF93YXZlMDFfc2NhbGVkW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxLCBdKQoKCiNhcHBseShZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZCAsMiwgd2hpY2gubWF4KQojYXBwbHkoWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQgLDIsIHdoaWNoLm1pbikKYGBgCgoKRm91bmQgdGhlIG91dGxpZXIgLSBsb29rcyBsaWtlIGl0J3MgNDQwCmBgYHtyLCB9CgpmaW5kT3V0bGllcnMgPC0gZnVuY3Rpb24oeCwgc2RzID0gNil7CiAgCiAgaXggPC0gd2hpY2goYWJzKHggLSBtZWFuKHgpKSA+IChzZHMgKiBzZCh4KSkpCiAgCn0KCmFwcGx5KFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkICwyLCBmaW5kT3V0bGllcnMsIHNkcyA9IDEwKQpgYGAKCgpgYGB7ciwgfQojIENyZWF0ZSBmaXQgbGlzdHMgZm9yIHRoZSBjb21iaW5lZCBkYXRhCllfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkX2xpc3QgPC0gbWF0Mmxpc3QoWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQpCgpmaXRfbGlzdF9jb25zdF9sZXZlbDFhX3dhdmUwMSA8LSBtY2xhcHBseShYID0gWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfbGlzdCAsIEZVTiA9IGttLCBmb3JtdWxhID0gfi4sIGRlc2lnbiA9IFhfbGV2ZWwxYV93YXZlMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWMuY29yZXMgPSA0LCBjb250cm9sID0gbGlzdCh0cmFjZSA9IEZBTFNFKSkKCgpgYGAKCiMjIExlYXZlLW9uZS1vdXQgYW5hbHlzZXMgb2YgZW11bGF0b3IgcHJlZGljdGlvbiBhY2N1cmFjeQoKYGBge3IsIH0KCmxvb2xpc3Rfa21fWV9sZXZlbDFhIDwtIG1jbGFwcGx5KFggPSBmaXRfbGlzdF9jb25zdF9sZXZlbDFhLCBGVU4gPSBsZWF2ZU9uZU91dC5rbSwgdHlwZSA9ICdVSycsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQoKbG9vbGlzdF9rbV9ZX2xldmVsMWFfd2F2ZTAxIDwtIG1jbGFwcGx5KFggPSBmaXRfbGlzdF9jb25zdF9sZXZlbDFhX3dhdmUwMSwgRlVOID0gbGVhdmVPbmVPdXQua20sIHR5cGUgPSAnVUsnLCB0cmVuZC5yZWVzdGltID0gVFJVRSkKCmBgYAoKCmBgYHtyLCB9Cgpsb29zdGF0c19rbV9ZX2xldmVsMWEgPC0gbGFwcGx5KGZpdF9saXN0X2NvbnN0X2xldmVsMWEsIEZVTiA9IGttTG9vU3RhdHMpCmxvb3N0YXRzX2ttX1lfbGV2ZWwxYV93YXZlMDEgPC0gbGFwcGx5KGZpdF9saXN0X2NvbnN0X2xldmVsMWFfd2F2ZTAxLCBGVU4gPSBrbUxvb1N0YXRzKQoKYGBgCgoKVGhlIHRvcCByb3cgc2hvd3MgdGhlIGxlYXZlLW9uZS1vdXQgcHJlZGljdGlvbiBhY2N1cmFjeSBvZiB0aGUgb3JpZ2luYWwgd2F2ZTAwIGVuc2VtYmxlLCBhbmQgdGhlIGxvd2VyIHJvdyB0aGUgZW50aXJlIHdhdmUwMCBBTkQgd2F2ZTAxIGVuc2VtYmxlIGNvbWJpbmVkLgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNiwgfQoKI3BkZihmaWxlID0gJ2ZpZ3Mva21sb29zdGF0c19ZX2xldmVsMWEucGRmJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnBhcihtZnJvdyA9IGMoMiw0KSwgbWFyID0gYygzLDQsMiwyKSwgb21hID0gYyg0LDQsNCwwLjEpKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYSkpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssIGldCiAgbG9vIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhW1tpXV0KICB5bGltIDwtIHJhbmdlKGMobG9vJG1lYW4gLSAoMipsb28kc2QpLCBsb28kbWVhbiArICgyKmxvbyRzZCkpICkKICBwbG90KHksIGxvbyRtZWFuLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9ICcnICwgeWxpbSA9IHlsaW0sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDEwMCksCiAgICAgICBwY2ggPSAxOSkKICBzZWdtZW50cyh4MCA9IHksIHkwID0gbG9vJG1lYW4gLSAoMipsb28kc2QpICAsIHgxID0geSAsIHkxID0gbG9vJG1lYW4gKyAoMipsb28kc2QpLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCA1MCkpCiAgYWJsaW5lKDAsMSkKICBsZWdlbmQoJ3RvcGxlZnQnLCBsZWdlbmQgPSBjb2xuYW1lcyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKVtpXSwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyICApCiAgbGVnZW5kKCdib3R0b21yaWdodCcsbGVnZW5kID0gcGFzdGUoJ3BtYWUgPScscm91bmQobG9vc3RhdHNfa21fWV9sZXZlbDFhW1tpXV0kcG1hZSwyKSwnJScpICwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyKQoKfQoKbXRleHQoJ0FjdHVhbCcsIHNpZGUgPSAxLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyICkKbXRleHQoJ1ByZWRpY3RlZCcsIHNpZGUgPSAyLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKbXRleHQoJ0xldmVsIDFhIGVuc2VtYmxlIG91dHB1dHMnLCBzaWRlID0gMywgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikKCiNkZXYub2ZmKCkKCiNwZGYoZmlsZSA9ICdmaWdzL2ttbG9vc3RhdHNfWV9sZXZlbDFhLnBkZicsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEyKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYSkpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbLCBpXQogIGxvbyA8LSBsb29saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDFbW2ldXQogIHlsaW0gPC0gcmFuZ2UoYyhsb28kbWVhbiAtICgyKmxvbyRzZCksIGxvbyRtZWFuICsgKDIqbG9vJHNkKSkgKQogIHBsb3QoeSwgbG9vJG1lYW4sIHhsYWIgPSAnJywgeWxhYiA9ICcnLCBtYWluID0gJycgLCB5bGltID0geWxpbSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwgMTAwKSwKICAgICAgIHBjaCA9IDE5KQogIHNlZ21lbnRzKHgwID0geSwgeTAgPSBsb28kbWVhbiAtICgyKmxvbyRzZCkgICwgeDEgPSB5ICwgeTEgPSBsb28kbWVhbiArICgyKmxvbyRzZCksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDEwMCkpCiAgYWJsaW5lKDAsMSkKICBsZWdlbmQoJ3RvcGxlZnQnLCBsZWdlbmQgPSBjb2xuYW1lcyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKVtpXSwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyICApCiAgbGVnZW5kKCdib3R0b21yaWdodCcsbGVnZW5kID0gcGFzdGUoJ3BtYWUgPScscm91bmQobG9vc3RhdHNfa21fWV9sZXZlbDFhX3dhdmUwMVtbaV1dJHBtYWUsMiksJyUnKSAsIGJ0eSA9ICduJywgdGV4dC5mb250ID0gMikKCn0KCm10ZXh0KCdBY3R1YWwnLCBzaWRlID0gMSwgbGluZSA9IDEsIG91dGVyID0gVFJVRSwgY2V4ID0gMiApCm10ZXh0KCdQcmVkaWN0ZWQnLCBzaWRlID0gMiwgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikgCm10ZXh0KCdMZXZlbCAxYSBlbnNlbWJsZSBvdXRwdXRzJywgc2lkZSA9IDMsIGxpbmUgPSAwLCBvdXRlciA9IFRSVUUsIGNleCA9IDIpCgpgYGAKCmBgYHtyfQojIHJlbW92ZSB0byBsZXZlbCAxYSBSZWxhdGl2ZSB0byB0b3BsZXZlbF9peCAtIHVzZWZ1bCBmb3IgcGxvdHRpbmcgZXRjLgojdG9wbGV2ZWxfdG9fbGV2ZWxfMWFfaXggPC0odG9wbGV2ZWxfaXhbLVlfbmxldmVsMF9peF0pW2xldmVsMWFfaXhdCgojIFNvIGZ1cnRoZXIgY29uc3RyYWluaW5nIHRvIGxldmVsIDIgY2FuIGJlIGFzc29jaWF0ZWQgYmFjayB0byB0aGUgdG9wIGxldmVsLgoKbGV2ZWwyX2l4IDwtIHdoaWNoKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCduYnBfbG5kX3N1bSddID4gMCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ25wcF9ubGltX2xuZF9zdW0nXSA+IDM1ICYgIFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCdjU29pbF9sbmRfc3VtJ10gPCAzMDAwICYKICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddID4gMzAwICYgWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddIDwgODAwCiAgICAgICAgICAgICAgICAgIAogICkKCmxldmVsMl9peF9sZXZlbDFhX3dhdmUwMSA8LSB3aGljaChZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ25icF9sbmRfc3VtJ10gPiAwICYKICAgICAgICAgICAgICAgICAgICBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ25wcF9ubGltX2xuZF9zdW0nXSA+IDM1ICYgWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbLCdjU29pbF9sbmRfc3VtJ10gPiA3NTAgJiBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSA8IDMwMDAgJgogICAgICAgICAgICAgICAgICBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddID4gMzAwICYgWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbLCdjVmVnX2xuZF9zdW0nXSA8IDgwMAogICAgICAgICAgICAgICAgICApCgpgYGAKCiMjIEVtdWxhdG9yIGFjY3VyYWN5IG9mIG1lbWJlcnMgZnJvbSB3YXZlIDAwIGFuZCB3YXZlIDAxIHRoYXQgcGFzcyBsZXZlbCAyIChBVydzKSBjb25zdHJhaW50cwoKV2Ugc2VlIHRoYXQgdGhlIGVycm9yIHN0YXRzIGZvciBzb21lIG9mIHRoZSBvdXRwdXRzIGZyb20gd2F2ZTAxIGFyZSB3b3JzZSwgYnV0IHRoZXJlIGFyZSBtYW55IG1vcmUgZW5zZW1ibGUgbWVtYmVycyB0aGF0IGxpZSB3aXRoaW4gdGhlIGNvbnN0cmFpbnRzIGZvciB3YXZlIDAxLgoKInBtYWUiIGlzICJwcm9wb3J0aW9uYWwgbWVhbiBhYnNvbHVlIGVycm9yIiwgd2hpY2ggaXMgdGhlIG1lYW4gYWJzb2x1dGUgZXJyb3IgZXhwcmVzc2VkIGFzIGEgcGVyY2VudGFnZSBvZiB0aGUgb3JpZ2luYWwgKG1pbmltYWxseSBjb25zdHJhaW5lZCkgZW5zZW1ibGUgcmFuZ2UgaW4gdGhhdCBvdXRwdXQuIAoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNiwgfQoKI3BkZihmaWxlID0gJ2ZpZ3Mva21sb29zdGF0c19ZX2xldmVsMWEucGRmJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnBhcihtZnJvdyA9IGMoMiw0KSwgbWFyID0gYygzLDQsMiwyKSwgb21hID0gYyg0LDQsNCwwLjEpKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYSkpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3NjYWxlZFtsZXZlbDJfaXgsIGldCiAgbG9vIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhW1tpXV0KICB5bGltIDwtIHJhbmdlKGMobG9vJG1lYW5bbGV2ZWwyX2l4XSAtICgyKmxvbyRzZFtsZXZlbDJfaXhdKSwgbG9vJG1lYW5bbGV2ZWwyX2l4XSArICgyKmxvbyRzZFtsZXZlbDJfaXhdKSkgKQogIHBsb3QoeSwgbG9vJG1lYW5bbGV2ZWwyX2l4XSwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnJyAsIHlsaW0gPSB5bGltLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCAxMDApLAogICAgICAgcGNoID0gMTkpCiAgc2VnbWVudHMoeDAgPSB5LCB5MCA9IGxvbyRtZWFuW2xldmVsMl9peF0gLSAoMipsb28kc2RbbGV2ZWwyX2l4XSkgICwgeDEgPSB5ICwgeTEgPSBsb28kbWVhbltsZXZlbDJfaXhdICsgKDIqbG9vJHNkW2xldmVsMl9peF0pLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCAxMDApKQogIGFibGluZSgwLDEpCiAgbGVnZW5kKCd0b3BsZWZ0JywgbGVnZW5kID0gY29sbmFtZXMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZClbaV0sIGJ0eSA9ICduJywgdGV4dC5mb250ID0gMiAgKQogIGxlZ2VuZCgnYm90dG9tcmlnaHQnLGxlZ2VuZCA9IHBhc3RlKCdwbWFlID0nLHJvdW5kKGxvb3N0YXRzX2ttX1lfbGV2ZWwxYVtbaV1dJHBtYWUsMiksJyUnKSAsIGJ0eSA9ICduJywgdGV4dC5mb250ID0gMikKCn0KCiNkZXYub2ZmKCkKCiNwZGYoZmlsZSA9ICdmaWdzL2ttbG9vc3RhdHNfWV9sZXZlbDFhLnBkZicsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEyKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYSkpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxLCBpXQogIGxvbyA8LSBsb29saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDFbW2ldXQogIHlsaW0gPC0gcmFuZ2UoYyhsb28kbWVhbltsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdIC0gKDIqbG9vJHNkW2xldmVsMl9peF9sZXZlbDFhX3dhdmUwMV0pLCBsb28kbWVhbltsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdICsgKDIqbG9vJHNkW2xldmVsMl9peF9sZXZlbDFhX3dhdmUwMV0pKSApCiAgcGxvdCh5LCBsb28kbWVhbltsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9ICcnICwgeWxpbSA9IHlsaW0sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDEwMCksCiAgICAgICBwY2ggPSAxOSkKICBzZWdtZW50cyh4MCA9IHksIHkwID0gbG9vJG1lYW5bbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxXSAtICgyKmxvbyRzZFtsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdKSAgLCB4MSA9IHkgLCB5MSA9IGxvbyRtZWFuW2xldmVsMl9peF9sZXZlbDFhX3dhdmUwMV0gKyAoMipsb28kc2RbbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxXSksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDUwKSkKICBhYmxpbmUoMCwxKQogIGxlZ2VuZCgndG9wbGVmdCcsIGxlZ2VuZCA9IGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpW2ldLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIgICkKICBsZWdlbmQoJ2JvdHRvbXJpZ2h0JyxsZWdlbmQgPSBwYXN0ZSgncG1hZSA9Jyxyb3VuZChsb29zdGF0c19rbV9ZX2xldmVsMWFfd2F2ZTAxW1tpXV0kcG1hZSwyKSwnJScpICwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyKQoKfQoKbXRleHQoJ0FjdHVhbCcsIHNpZGUgPSAxLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyICkKbXRleHQoJ1ByZWRpY3RlZCcsIHNpZGUgPSAyLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKbXRleHQoJ0xldmVsIDIgY29uc3RyYWluZWQgZW5zZW1ibGUgb3V0cHV0cycsIHNpZGUgPSAzLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKQoKYGBgCgojIyBEb2VzIHRoZSBlbXVsYXRvciBpbXByb3ZlIGlzIHlvdSBsb29rIGF0IG9ubHkgdGhlIDM3IG1lbWJlcnMgdGhhdCBwYXNzIGxldmVsIDIgY29uc3RyYWludHMgaW4gd2F2ZSAwMD8KVGhpcyBnaXZlcyB1cyBhbiBpZGVhIG9mIGhvdyBnb29kIHRoZSBlbXVsYXRvciBpcyB3aGVyZSBpdCByZWFsbHkgbWF0dGVycywgYW5kIGFzIHRoZSBtZW1iZXJzIGFyZSBjb25zaXN0ZW50LCBnaXZlcyB1cyBhIGZhaXJlciBpZGVhIG9mIHdoZXRoZXIgdGhlIGVtdWxhdG9ycyBoYXZlIGltcHJvdmVkIHdpdGggbW9yZSBtZW1iZXJzLgoKR29vZCBuZXdzIGlzLCB0aGUgZW11bGF0b3JzIGFyZSBtb3JlIGFjY3VyYXRlIGZvciB3YXZlMDEuCgpgYGB7ciwgfQoKCmttTG9vU3RhdHNTdWJzZXQgPC0gZnVuY3Rpb24gKGttLCBpeCwgdHlwZSA9ICJVSyIpIAp7CiAgIyBDYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBhIHN1YnNldCBvZiB0aGUgbWVtYmVycyBvZiBhIGttIGZpdCBsaXN0CiAgICBsb28gPC0gbGVhdmVPbmVPdXQua20oa20sIHR5cGUgPSB0eXBlLCB0cmVuZC5yZWVzdGltID0gVFJVRSkKICAgIHByZWRkaWZmIDwtIGxvbyRtZWFuW2l4XSAtIGttQHlbaXhdCiAgICBtYWUgPC0gbWVhbihhYnMocHJlZGRpZmYpKQogICAgcm1zZSA8LSBzcXJ0KG1lYW4ocHJlZGRpZmZeMikpCiAgICBtYXhlcnIgPC0gbWF4KHByZWRkaWZmKQogICAgYWJzZGlmZiA8LSBhYnMoZGlmZihyYW5nZShrbUB5KSkpCiAgICBwbWFlIDwtIChtYWUvYWJzZGlmZikgKiAxMDAKICAgIHJldHVybihsaXN0KGxvbyA9IGxvbywgbWFlID0gbWFlLCBwbWFlID0gcG1hZSwgbWF4ZXJyID0gbWF4ZXJyKSkKfQoKCmBgYAoKCmBgYHtyLCB9Cgpsb29saXN0X2ttX1lfbGV2ZWwxYV9sZXZlbDIgPC0gcmFwcGx5KGxvb2xpc3Rfa21fWV9sZXZlbDFhLCBmID0gZnVuY3Rpb24oeCkgeFtsZXZlbDJfaXhdLCBob3cgPSAibGlzdCIpCgpsb29saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDFfbGV2ZWwyIDwtIHJhcHBseShsb29saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDEsIGYgPSBmdW5jdGlvbih4KSB4W2xldmVsMl9peF0sIGhvdyA9ICJsaXN0IikKCgpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyLCB9CgojcGRmKGZpbGUgPSAnZmlncy9rbWxvb3N0YXRzX1lfbGV2ZWwxYS5wZGYnLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKcGFyKG1mcm93ID0gYygyLDIpLCBtYXIgPSBjKDMsNCwyLDIpLCBvbWEgPSBjKDQsNCw0LDAuMSkpCmZvcihpIGluIDE6bGVuZ3RoKGxvb2xpc3Rfa21fWV9sZXZlbDFhX2xldmVsMikpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3NjYWxlZFtsZXZlbDJfaXgsIGldCiAgCiAgbG9vIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhX2xldmVsMltbaV1dCiAgeWxpbSA8LSByYW5nZShjKGxvbyRtZWFuLSAoMipsb28kc2QpLCBsb28kbWVhbiArICgyKmxvbyRzZCkpICkKICBwbG90KHksIGxvbyRtZWFuLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9ICcnICwgeWxpbSA9IHlsaW0sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDI1MCksCiAgICAgICBwY2ggPSAxOSkKICBhcnJvd3MoeDAgPSB5LCB5MCA9IGxvbyRtZWFuIC0gKDIqbG9vJHNkKSAgLCB4MSA9IHkgLCB5MSA9IGxvbyRtZWFuICsgKDIqbG9vJHNkKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwgMTUwKSAsICBhbmdsZSA9IDkwLCBjb2RlID0gMywgbGVuZ3RoID0gMC4wMykKICAKICB5MSA8LSBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFtsZXZlbDJfaXgsIGldCiAgbG9vIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMV9sZXZlbDJbW2ldXQogIAogICAgcG9pbnRzKHkxLCBsb28kbWVhbiwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnJyAsIHlsaW0gPSB5bGltLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLCAyNTApLAogICAgICAgcGNoID0gMTkpCiAgYXJyb3dzKHgwID0geSwgeTAgPSBsb28kbWVhbiAtICgyKmxvbyRzZCkgICwgeDEgPSB5ICwgeTEgPSBsb28kbWVhbiArICgyKmxvbyRzZCksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDI1MCksICBhbmdsZSA9IDkwLCBjb2RlID0gMywgbGVuZ3RoID0gMC4wMykKICAKICAKICBhYmxpbmUoMCwxKQogIGxlZ2VuZCgndG9wbGVmdCcsIGxlZ2VuZCA9IGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpW2ldLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIgICkKICBsZWdlbmQoJ2JvdHRvbXJpZ2h0JyxsZWdlbmQgPSBwYXN0ZSgncG1hZSA9Jyxyb3VuZChsb29zdGF0c19rbV9ZX2xldmVsMWFbW2ldXSRwbWFlLDIpLCclJykgLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIpCgp9CgptdGV4dCgnQWN0dWFsJywgc2lkZSA9IDEsIGxpbmUgPSAxLCBvdXRlciA9IFRSVUUsIGNleCA9IDIgKQptdGV4dCgnUHJlZGljdGVkJywgc2lkZSA9IDIsIGxpbmUgPSAwLCBvdXRlciA9IFRSVUUsIGNleCA9IDIpIAptdGV4dCgnTGV2ZWwgMiB3YXZlIDAwIGVuc2VtYmxlIG91dHB1dHMnLCBzaWRlID0gMywgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikKCnJlc2V0KCkKbGVnZW5kKCd0b3BsZWZ0JywgcGNoID0gMTksIGxlZ2VuZCA9IGMoJ3dhdmUwMCcsICd3YXZlMDEnKSwgY29sID0gYyh3YXZlMDBjb2wsIHdhdmUwMWNvbCApLCBob3JpeiA9IFRSVUUpCgoKYGBgCgpUaGVzZSBsZWF2ZS1vbmUtb3V0IHByZWRpY3Rpb24gYWNjdXJhY3kgcGxvdHMgcmFuayB0aGUgZW5zZW1ibGUgbWVtYmVycyBmcm9tIGxhcmdlc3QgdW5kZXJwcmVkaWN0aW9uIHRvIGxhcmdlc3Qgb3ZlcnByZWRpY3Rpb24gdXNpbmcgdGhlIHdhdmUwMCBwcmVkaWN0aW9ucy4gQSBwZXJmZWN0IHByZWRpY3Rpb24gd291bGQgYXBwZWFyIG9uIHRoZSBob3Jpem9udGFsICJ6ZXJvIiBsaW5lLgoKTWFueSBvZiB0aGUgd2F2ZTAxIHByZWRpY3Rpb25zIGFyZSBjbG9zZXIgdG8gdGhlIGhvcml6b250YWwgbGluZSwgYW5kIHRoZXJlZm9yZSBtb3JlIGFjY3VyYXRlIHByZWRpY3Rpb25zLiAgCgpOb25lIG9mIHRoZSBwcmVkaWN0aW9ucyBhcmUgb3V0c2lkZSB0aGUgdW5jZXJ0YWludHkgYm91bmRzLCB3aGljaCBzdWdnZXN0cyB0aGV5IGFyZSBvdmVyY29uc2VydmF0aXZlIChzaG91bGQgYmUgc21hbGxlcikuCgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTAsIH0KCiNwZGYoZmlsZSA9ICdmaWdzL2ttbG9vc3RhdHNfWV9sZXZlbDFhLnBkZicsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEyKQpwYXIobWZyb3cgPSBjKDQsMSksIG1hciA9IGMoMyw0LDIsMiksIG9tYSA9IGMoNCw0LDQsMC4xKSkKZm9yKGkgaW4gMTpsZW5ndGgobG9vbGlzdF9rbV9ZX2xldmVsMWFfbGV2ZWwyKSl7CiAgCiAgeSA8LSBZX2NvbnN0X2xldmVsMWFfc2NhbGVkW2xldmVsMl9peCwgaV0KCiAgbG9vXzAwIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhX2xldmVsMltbaV1dCiAgbG9vXzAxIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMV9sZXZlbDJbW2ldXQogIAogIHByZWRkaWZmX3dhdmUwMCA8LSB5IC0gbG9vXzAwJG1lYW4KICBwcmVkZGlmZl93YXZlMDEgPC0geSAtIGxvb18wMSRtZWFuCiAgCiAgICAjIHJhbmsgYnkgdGhlIG9yaWdpbmFsIHdhdmUgMDAgcHJlZGljdGlvbnMKICBsb29fcmFua19peCA8LSBzb3J0KHByZWRkaWZmX3dhdmUwMCAsIGluZGV4LnJldHVybiA9IFRSVUUpCiAgCiAgIHlsaW0gPC0gcmFuZ2UoYyhwcmVkZGlmZl93YXZlMDBbbG9vX3JhbmtfaXgkaXhdIC0gKDIqbG9vXzAwJHNkW2xvb19yYW5rX2l4JGl4XSksCiAgICAgICAgICAgICAgICAgICBwcmVkZGlmZl93YXZlMDBbbG9vX3JhbmtfaXgkaXhdICsgKDIqbG9vXzAwJHNkW2xvb19yYW5rX2l4JGl4XSksCiAgICAgICAgICAgICAgICAgICBwcmVkZGlmZl93YXZlMDFbbG9vX3JhbmtfaXgkaXhdIC0gKDIqbG9vXzAxJHNkW2xvb19yYW5rX2l4JGl4XSksCiAgICAgICAgICAgICAgICAgICBwcmVkZGlmZl93YXZlMDFbbG9vX3JhbmtfaXgkaXhdICsgKDIqbG9vXzAxJHNkW2xvb19yYW5rX2l4JGl4XSkKICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICApCiAgIAogICBwbG90KHByZWRkaWZmX3dhdmUwMFtsb29fcmFua19peCRpeF0sIHhsYWIgPSAnJywgeWxhYiA9ICcnLCBtYWluID0gJycgLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCAyNTUpLAogICAgICAgIHBjaCA9IDE5LCB5bGltID0geWxpbSkKICAgCiAgIGFibGluZShoID0gMCkKICAgCiAgYXJyb3dzKHgwID0gMTpsZW5ndGgoeSksIHkwID0gcHJlZGRpZmZfd2F2ZTAwW2xvb19yYW5rX2l4JGl4XSAtICgyKmxvb18wMCRzZFtsb29fcmFua19peCRpeF0pICAsIHgxID0gMTpsZW5ndGgoeSkgLCB5MSA9IHByZWRkaWZmX3dhdmUwMFtsb29fcmFua19peCRpeF0gKyAoMipsb29fMDAkc2RbbG9vX3JhbmtfaXgkaXhdKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwgMTUwKSwgIGFuZ2xlID0gOTAsIGNvZGUgPSAzLCBsZW5ndGggPSAwLjAzKQogICAKICBwb2ludHMocHJlZGRpZmZfd2F2ZTAxW2xvb19yYW5rX2l4JGl4XSwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnJyAsIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDI1NSksCiAgICAgICAgcGNoID0gMTkpCiAgCiAgICBhcnJvd3MoeDAgPSAxOmxlbmd0aCh5KSwgeTAgPSBwcmVkZGlmZl93YXZlMDFbbG9vX3JhbmtfaXgkaXhdIC0gKDIqbG9vXzAxJHNkW2xvb19yYW5rX2l4JGl4XSkgICwgeDEgPSAxOmxlbmd0aCh5KSAsIHkxID0gcHJlZGRpZmZfd2F2ZTAxW2xvb19yYW5rX2l4JGl4XSArICgyKmxvb18wMSRzZFtsb29fcmFua19peCRpeF0pLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLCAxNTApLCBhbmdsZSA9IDkwLCBjb2RlID0gMywgbGVuZ3RoID0gMC4wMykKICAgCiAgIG10ZXh0KGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpW2ldLCBzaWRlID0gMywgYWRqID0gMCwgbGluZSA9IDEpCiAgCgp9CgoKIHJlc2V0KCkKIGxlZ2VuZCgndG9wbGVmdCcsIHBjaCA9IDE5LCBsZWdlbmQgPSBjKCd3YXZlMDAnLCAnd2F2ZTAxJyksIGNvbCA9IGMod2F2ZTAwY29sLCB3YXZlMDFjb2wgKSwgaG9yaXogPSBUUlVFKQoKYGBgCgoKYGBge3IsIH0KCmxvb3N0YXRzX2ttX1lfbGV2ZWwxYV9zdWIgPC0gbGFwcGx5KGZpdF9saXN0X2NvbnN0X2xldmVsMWEsIEZVTiA9IGttTG9vU3RhdHNTdWJzZXQsIGl4ID0gbGV2ZWwyX2l4KQpsb29zdGF0c19rbV9ZX2xldmVsMWFfd2F2ZTAxX3N1YiA8LSBsYXBwbHkoZml0X2xpc3RfY29uc3RfbGV2ZWwxYV93YXZlMDEsIEZVTiA9IGttTG9vU3RhdHNTdWJzZXQsIGl4ID0gbGV2ZWwyX2l4KQoKYGBgCgpMb29raW5nIGF0IHRoZSBwcm9wb3J0aW9uYWwgbWVhbiBhYnNvbHV0ZSBlcnJvciAocG1hZSksIGV4cHJlc3NlZCBpbiBwZXJjZW50LCB3ZSBjYW4gc2VlIHRoYXQgaXQgZG9lc24ndCBpbXByb3ZlIG11Y2ggZm9yIHRoZSB3aG9sZSBlbnNlbWJsZSwgYnV0ICpkb2VzKiBpbXByb3ZlIHNpZ25pZmljYW50bHkgZm9yIHRoZSBzdWJzZXQgb2YgZW5zZW1ibGUgbWVtYmVycyB0aGF0IGZhbGwgd2l0aGluIEFXJ3MgY29uc3RyYWludHMgZnJvbSB0aGUgZmlyc3QgZW5zZW1ibGUgKG1hcmtlZCAiX3N1YiIpLgoKYGBge3IsIGVjaG8gPSBUUlVFLCB9CgpwbWFlX3dhdmUwMCA8LSBsYXBwbHkobG9vc3RhdHNfa21fWV9sZXZlbDFhLCBGVU4gPSBmdW5jdGlvbih4KSB4JHBtYWUgKQpwbWFlX3dhdmUwMSA8LSBsYXBwbHkobG9vc3RhdHNfa21fWV9sZXZlbDFhX3dhdmUwMSwgRlVOID0gZnVuY3Rpb24oeCkgeCRwbWFlICkKCnBtYWVfd2F2ZTAwX3N1YiA8LSBsYXBwbHkobG9vc3RhdHNfa21fWV9sZXZlbDFhX3N1YiwgRlVOID0gZnVuY3Rpb24oeCkgeCRwbWFlICkKcG1hZV93YXZlMDFfc3ViIDwtIGxhcHBseShsb29zdGF0c19rbV9ZX2xldmVsMWFfd2F2ZTAxX3N1YiwgRlVOID0gZnVuY3Rpb24oeCkgeCRwbWFlICkKCnBtYWVfdGFibGUgPC0gY2JpbmQocG1hZV93YXZlMDAsIHBtYWVfd2F2ZTAxLCBwbWFlX3dhdmUwMF9zdWIsIHBtYWVfd2F2ZTAxX3N1YikKCnByaW50KHBtYWVfdGFibGUpCgpgYGAKCiMgQ29tcGFyaW5nIGF0bW9zcGhlcmljIGdyb3d0aCBpbiB3YXZlMDAsIHdhdmUwMSBhbmQgb2JzZXJ2YXRpb25zCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFLCBldmFsPXBhcmFtcyRsaW1pdH0KaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0IDwtIHJlYWRfZXhjZWwoJ0dsb2JhbF9DYXJib25fQnVkZ2V0XzIwMjB2MS4wLnhsc3gnLCBzaGVldCA9ICJIaXN0b3JpY2FsIEJ1ZGdldCIsIHNraXAgPSAxNSwgbl9tYXggPSAyNzApCgpyZXNpZCA8LSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYG9jZWFuIHNpbmtgIC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBmb3NzaWwgZW1pc3Npb25zIGV4Y2x1ZGluZyBjYXJib25hdGlvbmAKCnJlc2lkX2l4IDwtIHdoaWNoKGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRZZWFyICVpbiUgeWVhcnMpCgpuZWdfYWcgPC0gc3dlZXAobmJwX2VucywgMiwgcmVzaWRbcmVzaWRfaXhdLCBGVU4gPSAnKycpCgoKbWF0cGxvdCh5ZWFycywgdCgtbmVnX2FnKSwgdHlwZSA9ICdsJywgbHR5ID0gJ3NvbGlkJyx5bGltID0gYygwLCA4KSwgY29sID0gd2F2ZTAwY29sLCAKICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2F0bW9zcGhlcmljIGdyb3d0aCcsIHhsYWIgPSAnJywKICAgICAgICAgYnR5ID0gJ24nLCB4bGltID0gYygxOTYwLCAyMDIwKSkKCmxpbmVzKGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRZZWFyLCBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGF0bW9zcGhlcmljIGdyb3d0aGAsIGNvbCA9ICdyZWQnKQoKCmBgYAoKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDgsIH0KCm9ic2NvbCA9ICdwdXJwbGUnCndhdmUwMV9sZXZlbDJjb2wgPC0gJ2dvbGQnCgphZyA8LSBtYXRyaXgobnJvdyA9IG5yb3cobmJwX2VucyksIG5jb2wgPSBuY29sKG5icF9lbnMpKQoKYWcwMSA8LSBtYXRyaXgobnJvdyA9IG5yb3cobmJwX2Vuc193YXZlMDEpLCBuY29sID0gbmNvbChuYnBfZW5zX3dhdmUwMSkpCgpmb3IoaSBpbiAxOm5yb3cobmJwX2VucykpewogIAphZ1tpLCBdIDwtIGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRgZm9zc2lsIGVtaXNzaW9ucyBleGNsdWRpbmcgY2FyYm9uYXRpb25gW3Jlc2lkX2l4XSAtIGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRgb2NlYW4gc2lua2BbcmVzaWRfaXhdIC0gIG5icF9lbnNbaSwgXQogIAp9CgoKZm9yKGkgaW4gMTpucm93KG5icF9lbnNfd2F2ZTAxKSl7CmFnMDFbaSwgXSA8LSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGZvc3NpbCBlbWlzc2lvbnMgZXhjbHVkaW5nIGNhcmJvbmF0aW9uYFtyZXNpZF9peF0gLSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYG9jZWFuIHNpbmtgW3Jlc2lkX2l4XSAtICBuYnBfZW5zX3dhdmUwMVtpLCBdCiAgCn0KCmFnX3N0YW4gPC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBmb3NzaWwgZW1pc3Npb25zIGV4Y2x1ZGluZyBjYXJib25hdGlvbmBbcmVzaWRfaXhdIC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBvY2VhbiBzaW5rYFtyZXNpZF9peF0gLSAgbmJwX3N0YW4KCiNwZGYod2lkdGggPSA4LCBoZWlnaHQgPSA2LCBmaWxlID0gJ2FnLnBkZicpCnBhcihsYXMgPSAxKQptYXRwbG90KHllYXJzLCB0KGFnW3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAwLCBdKSwgdHlwZSA9ICdsJywgbHR5ID0gJ3NvbGlkJyx5bGltID0gYygtMiwgMTApLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLDEwMCksIAogICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnYXRtb3NwaGVyaWMgZ3Jvd3RoJywgeGxhYiA9ICcnLAogICAgICAgICBidHkgPSAnbicsIHhsaW0gPSBjKDE5NjAsIDIwMTMpKQoKbWF0bGluZXMoeWVhcnMsIHQoYWcwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSwgXSksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsMTAwKSwgbHR5ID0gJ3NvbGlkJykKCm1hdGxpbmVzKHllYXJzLCB0KGFnMDFbbGV2ZWwyYV9peF93YXZlMDEsIF0pLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxX2xldmVsMmNvbCwgMTAwKSAsIGx0eSA9ICdzb2xpZCcpCgpsaW5lcyhoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkWWVhciwgaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBhdG1vc3BoZXJpYyBncm93dGhgLCBjb2wgPSBvYnNjb2wsIGx3ZCA9MikKCmxpbmVzKHllYXJzLCBhZ19zdGFuLCBjb2wgPSBzdGFuY29sLCBsd2QgPTIpCgoKbGVnZW5kKCd0b3BsZWZ0JywgbGVnZW5kID0gYygnd2F2ZTAwJywgJ3dhdmUwMScsICd3YXZlMDEgY29uc3RyYWluZWQnLCAnc3RhbmRhcmQnLCAnR0NCJyksIGNvbCA9IGMod2F2ZTAwY29sLCB3YXZlMDFjb2wsIHdhdmUwMV9sZXZlbDJjb2wsIHN0YW5jb2wsIG9ic2NvbCksIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCiNkZXYub2ZmKCkKCmBgYAoKCiMjIEFuZHkgYXNrcyAtIHdoYXQgY29uc3RyYWludCBkb2VzIHRoYXQgZ2l2ZSB1cyBpbiBjdW11bGF0aXZlIE5CUD8KCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNywgfQoKY3VtdWxhdGl2ZV9uYnBfZW5zIDwtIHQoYXBwbHkobmJwX2VucywgMSwgY3Vtc3VtKSkKCmN1bXVsYXRpdmVfbmJwX2Vuc193YXZlMDEgPC0gdChhcHBseShuYnBfZW5zX3dhdmUwMSwgMSwgY3Vtc3VtKSkKCmN1bXVsYXRpdmVfbmJwX3N0YW4gPC0gY3Vtc3VtKG5icF9zdGFuKQoKbWF0cGxvdCh5ZWFycywgdChjdW11bGF0aXZlX25icF9lbnMpLCB0eXBlID0gJ2wnLCBsdHkgPSAnc29saWQnLHlsaW0gPSBjKC0xMzAsIDI1MCksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsMTUwKSwgCiAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdjdW11bGF0aXZlIE5CUCcsIHhsYWIgPSAnJywKICAgICAgICAgYnR5ID0gJ24nLCB4bGltID0gYygxODUwLCAyMDIwKSkKCm1hdGxpbmVzKHllYXJzLCB0KGN1bXVsYXRpdmVfbmJwX2Vuc193YXZlMDEpLAogICAgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLDE1MCksCiAgICAgICAgIGx0eSA9ICdzb2xpZCcpCgptYXRsaW5lcyh5ZWFycywgdChjdW11bGF0aXZlX25icF9lbnNfd2F2ZTAxW2xldmVsMl9peF93YXZlMDEsIF0pLAogICAgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxX2xldmVsMmNvbCwgMTUwKSwKICAgICAgICAgbHR5ID0gJ3NvbGlkJykKCgpsaW5lcyh5ZWFycywgY3VtdWxhdGl2ZV9uYnBfc3RhbiwgY29sID0gbWFrZVRyYW5zcGFyZW50KHN0YW5jb2wsIDIwMCksIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDEuNSApCgoKbGVnZW5kKCd0b3BsZWZ0JywgbGVnZW5kID0gYygnd2F2ZTAwJywgJ3dhdmUwMScsICd3YXZlMDEgY29uc3RyYWluZWQnLCAnc3RhbmRhcmQnKSwgY29sID0gYyh3YXZlMDBjb2wsIHdhdmUwMWNvbCwgd2F2ZTAxX2xldmVsMmNvbCwgc3RhbmNvbCksIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDEuNSkKCmBgYAoKIyMgRWRkeSBzdWdnZXN0cyBtZWFzdXJpbmcgY3VtdWxhdGl2ZSBOQlAgYWdhaW5zdCBhdG1vc3BoZXJpYyBncm93dGggcmF0ZQoKYGBge3IsIH0KCiMgQ3VtdWxhdGl2ZSBOQlAgYXQgdGhlIGVuZCBvZiB0aGUgcnVuCmNuYnBfbW9kZXJuX2VucyA8LSBhcHBseShjdW11bGF0aXZlX25icF9lbnNbLCAxMzU6MTY0XSwgMSwgbWVhbikKCmNuYnBfbW9kZXJuX3dhdmUwMSA8LSBhcHBseShjdW11bGF0aXZlX25icF9lbnNfd2F2ZTAxWywgMTM1OjE2NF0sIDEsIG1lYW4pCgpjbmJwX21vZGVybl9zdGFuIDwtIG1lYW4oY3VtdWxhdGl2ZV9uYnBfc3RhblsxMzU6MTY0XSkKCgoKYGBgCgpDYWxjdWxhdGUgdGhlIGF0bW9zcGhlcmljIGdyb3d0aCByYXRlIG9mIDE5ODQtIDIwMTMgdXNpbmcgYSBzaW1wbGUgbGluZWFyIGZpdAoKYGBge3IsIH0KIyBhZ3IgPSBhdG1vc3BoZXJpYyBncm93dGggcmF0ZQojIGFnaWF2ID0gYXRtb3NwaGVyaWMgZ3Jvd3RoIGludGVyYW5udWFsIHZhcmlhYmlsaXR5CgphZ3JfbW9kZXJuX2VucyA8LSByZXAoTkEsIGxlbmd0aCA9IG5yb3coYWcpKQphZ2lhdl9tb2Rlcm5fZW5zIDwtIHJlcChOQSwgbGVuZ3RoID0gbnJvdyhhZykpCgpmb3IoaSBpbiAxOm5yb3coYWcpKXsKICAKICBkYXQgPC0gZGF0YS5mcmFtZSh0ID0xOjMwLCBhZyA9ICBhZ1tpLCAxMzU6MTY0XSkKICBmaXQgPC0gbG0oYWcgfiB0LCBkYXRhID0gZGF0KQogIGFncl9tb2Rlcm5fZW5zW2ldIDwtIGNvZWYoZml0KVsndCddCiAgYWdpYXZfbW9kZXJuX2Vuc1tpXSA8LSBzZChmaXQkcmVzaWR1YWxzKQp9CgoKYWdyX21vZGVybl93YXZlMDEgPC0gcmVwKE5BLCBsZW5ndGggPSBucm93KGFnMDEpKQphZ2lhdl9tb2Rlcm5fd2F2ZTAxIDwtIHJlcChOQSwgbGVuZ3RoID0gbnJvdyhhZzAxKSkKCmZvcihpIGluIDE6bnJvdyhhZzAxKSl7CiAgCiAgZGF0IDwtIGRhdGEuZnJhbWUodCA9MTozMCwgYWcgPSAgYWcwMVtpLCAxMzU6MTY0XSkKICBmaXQgPC0gbG0oYWcgfiB0LCBkYXRhID0gZGF0KQogIGFncl9tb2Rlcm5fd2F2ZTAxW2ldIDwtIGNvZWYoZml0KVsndCddCiAgYWdpYXZfbW9kZXJuX3dhdmUwMVtpXSA8LSBzZChmaXQkcmVzaWR1YWxzKQp9CgpkYXQgPC0gZGF0YS5mcmFtZSh0ID0xOjMwLCBhZyA9ICBhZ19zdGFuWzEzNToxNjRdKQphZ3Jfc3Rhbl9maXQgPC0gbG0oYWcgfiB0LCBkYXRhID0gZGF0KSAKYWdyX3N0YW4gPC0gY29lZihhZ3Jfc3Rhbl9maXQpWyd0J10KYWdpYXZfc3RhbiA8LSBzZChhZ3Jfc3Rhbl9maXQkcmVzaWR1YWxzKQogIAogIApgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNywgfQoKcGxvdChhZ3JfbW9kZXJuX2VucywgY25icF9tb2Rlcm5fZW5zLCAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwxNTApLAogICAgIHlsaW0gPSBjKC0xMjAsMjIwKSwgcGNoID0gMTksCiAgICAgeGxhYiA9ICdBdG1vc3BoZXJpYyBncm93dGggcmF0ZSAxOTg0LTIwMTQnLAogICAgIHlsYWIgPSAnQ3VtdWxhdGl2ZSBOQlAgZnJvbSBwcmVpbmR1c3RyaWFsIGJ5IDE5ODQtMjAxMycKICAgICApCgpjbmJwX3dhdmUwMV9jb2xzIDwtIHJlcCh3YXZlMDFjb2wsIGxlbmd0aChjbmJwX21vZGVybl93YXZlMDEpKQpjbmJwX3dhdmUwMV9jb2xzW2xldmVsMl9peF93YXZlMDFdIDwtIHdhdmUwMV9sZXZlbDJjb2wgCgpwb2ludHMoIGFncl9tb2Rlcm5fd2F2ZTAxLGNuYnBfbW9kZXJuX3dhdmUwMSwgY29sID0gbWFrZVRyYW5zcGFyZW50KGNuYnBfd2F2ZTAxX2NvbHMsIDE1MCksIHBjaCA9IDE5KQpwb2ludHMoIGFncl9zdGFuLGNuYnBfbW9kZXJuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIHBjaCA9IDE5LCBjZXggPSAxLjUpCgpsZWdlbmQoJ3RvcGxlZnQnLCBsZWdlbmQgPSBjKCd3YXZlMDAnLCAnd2F2ZTAxJywgJ3dhdmUwMSBjb25zdHJhaW5lZCcsICdzdGFuZGFyZCcpLCBjb2wgPSBjKHdhdmUwMGNvbCwgd2F2ZTAxY29sLCB3YXZlMDFfbGV2ZWwyY29sLCBzdGFuY29sKSwgcGNoID0gMTkpCgpwcmludChjKCdjb3JyZWxhdGlvbiBhZ3IgdnMgY25icCAoYWxsIG1lbWJlcnMpJywgY29yKGFncl9tb2Rlcm5fZW5zLCBjbmJwX21vZGVybl9lbnMpKSkKCnByaW50KGMoJ2NvcnJlbGF0aW9uIGFnciB2cyBjbmJwICh3YXZlMDEpJywgY29yKGFncl9tb2Rlcm5fd2F2ZTAxLCBjbmJwX21vZGVybl93YXZlMDEpKSkKCmZpdF93YXZlMDAgPC0gbG0oYWdyX21vZGVybl9lbnMgfiBjbmJwX21vZGVybl9lbnMpCmZpdF93YXZlMDEgPC0gbG0oYWdyX21vZGVybl93YXZlMDEgfiBjbmJwX21vZGVybl93YXZlMDEpCgpwcmludChzdW1tYXJ5KGZpdF93YXZlMDApKQpwcmludChzdW1tYXJ5KGZpdF93YXZlMDEpKQoKYGBgCkludGVyYW5udWFsIHZhcmlhYmlsaXR5IGFuZCBjdW11bGF0aXZlIE5CUCAgCgooY29ycmVsYXRpb25zIGFyZSBjbG9zZSB0byB6ZXJvLCBlc3BlY2lhbGx5IGluIHRoZSBsYXRlciB3YXZlKQoKCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNywgfQoKcGxvdChhZ2lhdl9tb2Rlcm5fZW5zLCBjbmJwX21vZGVybl9lbnMsICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLDE1MCksCiAgICAgeWxpbSA9IGMoLTEyMCwyMjApLCBwY2ggPSAxOSwKICAgICB4bGFiID0gJ0F0bW9zcGhlcmljIGdyb3d0aCBJQVYgMTk4NC0yMDE0JywKICAgICB5bGFiID0gJ0N1bXVsYXRpdmUgTkJQIGZyb20gcHJlaW5kdXN0cmlhbCBieSAxOTg0LTIwMTMnCiAgICAgKQoKcG9pbnRzKCBhZ2lhdl9tb2Rlcm5fd2F2ZTAxLCBjbmJwX21vZGVybl93YXZlMDEsIGNvbCA9IG1ha2VUcmFuc3BhcmVudChjbmJwX3dhdmUwMV9jb2xzLCAxNTApLCBwY2ggPSAxOSkKcG9pbnRzKCBhZ2lhdl9zdGFuLGNuYnBfbW9kZXJuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIHBjaCA9IDE5LCBjZXggPSAxLjUpCgpsZWdlbmQoJ2JvdHRvbXJpZ2h0JywgbGVnZW5kID0gYygnd2F2ZTAwJywgJ3dhdmUwMScsICd3YXZlMDEgY29uc3RyYWluZWQnLCAnc3RhbmRhcmQnKSwgY29sID0gYyh3YXZlMDBjb2wsIHdhdmUwMWNvbCwgd2F2ZTAxX2xldmVsMmNvbCwgc3RhbmNvbCksIHBjaCA9IDE5KQoKYGBgCgoKYGBge3IsIH0KCgpwcmludChjb3IoYWdpYXZfbW9kZXJuX2VucywgY25icF9tb2Rlcm5fZW5zKSkKcHJpbnQoY29yKCBhZ2lhdl9tb2Rlcm5fd2F2ZTAxLCBjbmJwX21vZGVybl93YXZlMDEpKQoKYGBgCgoKIyMgSG93IGNsb3NlIGNhbiB3ZSBnZXQgdGhlIG1vZGVsIHRvIHJlYWxpdHk/CgpVc2luZyBBdG1vc3BoZXJpYyBHcm93dGggUmF0ZSBhcyBhbiBleGFtcGxlLCBob3cgY2xvc2UgY2FuIHdlIGdldCB0aGUgbW9kZWwgdG8gb2JzZXJ2YXRpb25zPyBDYW4gd2UgZG8gYmV0dGVyIHRoYW4gc3RhbmRhcmQ/IFdoYXQgYXJlIHRoZSB0cmFkZSBvZmZzIG9mIGRvaW5nIHNvPyBIb3cgZG9lcyBnZXR0aW5nIGNsb3NlIGluIEFHUiBhZmZlY3QgcGVyZm9ybWFuY2UgaW4gb3RoZXIgb3V0cHV0cz8KCmBgYHtyLCB9CiMgRGVmaW5lIHRoZSBvYnNlcnZlZCBhdG1vc3BoZXJpYyBncm93dGggcmF0ZS4KCmFnX29icyA8LSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGF0bW9zcGhlcmljIGdyb3d0aGBbd2hpY2goaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JFllYXIgJWluJSB5ZWFycyldCgojIE1vZGVsIGRlcGFydHVyZSBmcm9tIG9ic2VydmF0aW9ucyBvZiBhdG1vc3BoZXJpYyBncm93dGgKYWdfZXJyIDwtIHN3ZWVwKGFnLCAyLCBhZ19vYnMsIEZVTiA9ICctJykKYWcwMV9lcnIgPC0gc3dlZXAoYWcwMSwgMiwgYWdfb2JzLCBGVU4gPSAnLScpCgoKbG9uZ19tb2Rlcm5feWVhcnMgPC0gMTk2MDoyMDEzCmFnX21vZGVybl9peCA8LSB3aGljaCggeWVhcnMgJWluJSBsb25nX21vZGVybl95ZWFycykKCmFnX2Vycl9tb2Rlcm4gPC0gYWdfZXJyWywgYWdfbW9kZXJuX2l4XQphZzAxX2Vycl9tb2Rlcm4gPC0gYWcwMV9lcnJbLCBhZ19tb2Rlcm5faXhdCgoKYWdfZXJyX3N0YW4gPC0gYWdfc3RhbiAtIGFnX29icwphZ19lcnJfc3Rhbl9tb2Rlcm4gPC0gYWdfZXJyX3N0YW5bYWdfbW9kZXJuX2l4XQoKYGBgCgpgYGB7ciwgfQptYXRwbG90KGxvbmdfbW9kZXJuX3llYXJzLCB0KGFnX2Vycl9tb2Rlcm5bd2l0aG91dF9vdXRsaWVyc19peF93YXZlMDAsXSksIHR5cGUgPSAnbCcsIGx0eSA9ICdzb2xpZCcsIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDEwMCksIG1haW4gPSAnQW1vc3BoZXJpYyBHcm93dGggRXJyb3InKQoKZXJyY29sIDwtIHJlcCh3YXZlMDFjb2wsIG5yb3coYWcwMVt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdKSkKI2VycmNvbFtsZXZlbDJfaXhfd2F2ZTAxXSA8LSB3YXZlMDFfbGV2ZWwyY29sCm1hdGxpbmVzKGxvbmdfbW9kZXJuX3llYXJzLCB0KGFnMDFfZXJyX21vZGVyblt3aXRob3V0X291dGxpZXJzX2l4X3dhdmUwMSxdKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KGVycmNvbCwgMTAwKSwgbHR5ID0gJ3NvbGlkJykKbWF0bGluZXMobG9uZ19tb2Rlcm5feWVhcnMsIHQoYWcwMV9lcnJfbW9kZXJuW2xldmVsMmFfaXhfd2F2ZTAxLCBdKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMV9sZXZlbDJjb2wsMTAwKSwgbHR5ID0gJ3NvbGlkJykKCmxpbmVzKGxvbmdfbW9kZXJuX3llYXJzLCBhZ19lcnJfc3Rhbl9tb2Rlcm4sIGNvbCA9IHN0YW5jb2wpCgpsZWdlbmQoJ2JvdHRvbWxlZnQnLCBsZWdlbmQgPSBjKCd3YXZlMDAnLCAnd2F2ZTAxJywgJ3dhdmUwMSBjb25zdHJhaW5lZCcsICdzdGFuZGFyZCcpLCBjb2wgPSBjKHdhdmUwMGNvbCwgd2F2ZTAxY29sLCB3YXZlMDFfbGV2ZWwyY29sLCBzdGFuY29sKSwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMS41KQoKYWJsaW5lKGg9MCkKCmBgYAoKCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNywgfQoKIyBBdG1vc3BoZXJpYyBncm93dGggbWVhbiBlcnJvcgphZ19tb2Rlcm5fbWUgPC0gYXBwbHkoYWdfZXJyX21vZGVybiwxLG1lYW4pCmFnMDFfbW9kZXJuX21lIDwtIGFwcGx5KGFnMDFfZXJyX21vZGVybiwxLCBtZWFuKQphZ19tb2Rlcm5fbWVfc3RhbiA8LSBtZWFuKGFnX2Vycl9zdGFuW2FnX21vZGVybl9peF0pCgoKIyBNZWFuIGFic29sdXRlIGVycm9yCmFnX21vZGVybl9tYWUgPC0gYXBwbHkoYWJzKGFnX2Vycl9tb2Rlcm4pLDEsbWVhbikKYWcwMV9tb2Rlcm5fbWFlIDwtIGFwcGx5KGFicyhhZzAxX2Vycl9tb2Rlcm4pLDEsIG1lYW4pCmFnX21vZGVybl9tYWVfc3RhbiA8LSBtZWFuKGFicyhhZ19lcnJfc3RhblthZ19tb2Rlcm5faXhdKSkKCgoKIyBSb290IG1lYW4gc3F1YXJlIGVycm9yCmFnX21vZGVybl9ybXNlIDwtIGFwcGx5KGFnX2Vycl9tb2Rlcm4sMSwgZnVuY3Rpb24oeCkgc3FydChtZWFuKHheMikpKQphZzAxX21vZGVybl9ybXNlIDwtIGFwcGx5KGFnMDFfZXJyX21vZGVybiwxLCBmdW5jdGlvbih4KSBzcXJ0KG1lYW4oeF4yKSkpCmFnX21vZGVybl9ybXNlX3N0YW4gPC0gc3FydChtZWFuKGFnX2Vycl9zdGFuW2FnX21vZGVybl9peF1eMikpCgojIFRoZXJlIGFyZSBzb21lIGJpZyBvdXRsaWVycyAKb3V0bGllcl9peF93YXZlMDEgPC0gd2hpY2goYWJzKGFnMDFfbW9kZXJuX21lICk+IDEwMCkKCnBhcihtZnJvdyA9IGMoMiwxKSkKaGlzdChhZ19tb2Rlcm5fbWUsIG1haW4gPSAnQXRtb3NwaGVyaWMgZ3Jvd3RoIG1lYW4gZXJyb3InLCBjb2wgPSB3YXZlMDBjb2wsIHhsaW0gPSBjKC0zLCAzKSkKcnVnKGFnX21vZGVybl9tZV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsd2QgPSAyKQpoaXN0KGFnMDFfbW9kZXJuX21lWy1vdXRsaWVyX2l4X3dhdmUwMV0sIGNvbCA9IHdhdmUwMWNvbCwgeGxpbSA9IGMoLTMsMyksIG1haW4gPSAnJykKcnVnKGFnX21vZGVybl9tZV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsd2QgPSAyKQoKcGFyKG1mcm93ID0gYygyLDEpKQpoaXN0KGFnX21vZGVybl9tYWUsIG1haW4gPSAnQXRtb3NwaGVyaWMgZ3Jvd3RoIG1lYW4gYWJzb2x1dGUgZXJyb3InLCBjb2wgPSB3YXZlMDBjb2wsIHhsaW0gPSBjKDAsIDMpKQpydWcoYWdfbW9kZXJuX21hZV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsd2QgPSAyKQpoaXN0KGFnMDFfbW9kZXJuX21hZVstb3V0bGllcl9peF93YXZlMDFdLCBjb2wgPSB3YXZlMDFjb2wsIGx3ZCA9IDIsIHhsaW0gPSBjKDAsMyksIG1haW4gPSAnJykKcnVnKGFnX21vZGVybl9tYWVfc3RhbiwgY29sID0gc3RhbmNvbCwgbHdkID0gMikKCnBhcihtZnJvdyA9IGMoMiwxKSkKaGlzdChhZ19tb2Rlcm5fcm1zZSwgeGxpbSA9IGMoMCwzKSwgY29sID0gd2F2ZTAwY29sLCBtYWluID0gJ0F0bW9zcGhlcmljIGdyb3d0aCBSTVNFJykKcnVnKGFnX21vZGVybl9ybXNlX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx3ZCA9IDIgKQpoaXN0KGFnMDFfbW9kZXJuX3Jtc2VbLW91dGxpZXJfaXhfd2F2ZTAxXSwgY29sID0gd2F2ZTAxY29sLCB4bGltID0gYygwLDMpLCBtYWluID0gJycpCnJ1ZyhhZ19tb2Rlcm5fcm1zZV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsd2QgPSAyICkKCgoKYGBgCgoKV2UndmUgZXN0YWJsaXNoZWQgdGhhdCBtb3N0IG9mIHRoZSBvcmlnaW5hbCBlbnNlbWJsZSBoYXZlIGFuIE1FL01BRS9STVNFIGxhcmdlciB0aGFuIHRoZSBzdGFuZGFyZCBydW4uIE1vcmUgKGJ1dCBmZXcpIG9mIHRoZSB3YXZlMDEgcGVyZm9ybSBiZXR0ZXIgdGhhbiBzdGFuZGFyZC4gCgpgYGB7ciwgfQoKCmJldHRlcl9peF9hZ19ybXNlIDwtIHdoaWNoKGFnX21vZGVybl9ybXNlIDwgYWdfbW9kZXJuX3Jtc2Vfc3RhbikKYmV0dGVyX2l4X2FnMDFfcm1zZSA8LSB3aGljaChhZzAxX21vZGVybl9ybXNlIDwgYWdfbW9kZXJuX3Jtc2Vfc3RhbikKCgpYX2JldHRlcl9hZyA8LSByYmluZChYW2JldHRlcl9peF9hZ19ybXNlLCBdLCBYX3dhdmUwMV90cmFpbltiZXR0ZXJfaXhfYWcwMV9ybXNlLCBdKQoKYGBgCgoKQSBtYXAgb2YgdGhlIDJEIHByb2plY3Rpb25zIG9mIHBhcmFtZXRlciBzcGFjZSB3aGVyZSB0aGUgZW5zZW1ibGUgbWVtYmVyIHBlcmZvcm1zIGJldHRlciB0aGFuIHN0YW5kYXJkLiAgCgpUaGUgYmx1ZSBwYXJ0IGlzIHRoZSBmaXJzdCB3YXZlLCBhbmQgbm90IHN1YmplY3QgdG8gY29uc3RyYWludCBzbyBtYXkgYmUgcmVtb3ZlZCBpbiB0aGUgc2Vjb25kIHdhdmUgKHdhdmUwMSkuCgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTIsIH0KCmJldHRlcl9peCA8LSAxOm5yb3coWF9iZXR0ZXJfYWcpCgpiZXR0ZXJfY29scyA8LSBjKHJlcCh3YXZlMDBjb2wsIGxlbmd0aChiZXR0ZXJfaXhfYWdfcm1zZSkpLCByZXAod2F2ZTAxY29sLGxlbmd0aChiZXR0ZXJfaXhfYWcwMV9ybXNlKSkpCgpwYWlycyhYX2JldHRlcl9hZywKICAgICAgY29sID0gYmV0dGVyX2NvbHMsCiAgICAgIGdhcCA9IDAsCiAgICAgIHhsaW0gPSBjKDAsMSksIHlsaW0gPSBjKDAsMSksCiAgICAgIHBjaCA9IDE5LAogICAgICBjZXg9IDAuOCwKICAgICAgbG93ZXIucGFuZWwgPSBOVUxMKQoKCgpgYGAKCiMjIEJ1aWxkIGVtdWxhdG9ycyBhbmQgZmluZCBwYXJ0cyBvZiBwYXJhbWV0ZXIgc3BhY2UgdGhhdCB0byBiZXR0ZXIgdGhhbiBzdGFuZGFyZCBhdCBhdG1vc3BoZXJpYyBncm93dGguCgpIYXZpbmcgdHJvdWJsZSBmaXR0aW5nIFJNU0UsIHRvIHRyeWluZyBtZWFuIGVycm9yLgoKV2h5IGlzIHRoZXJlIGFuIG9kZCBjb2xsZWN0aW9uIGF0IGp1c3QgdW5kZXIgMT8KCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyLCB9CiMgdGhlcmUgYXJlIDEwMCB3YXZlMDEgZW5zZW1ibGUgbWVtYmVycyB0aGF0IHBhc3MgdGhlIGxldmVsIDIgY29uc3RyYWluc3QKbGVuZ3RoKGxldmVsMl9peF93YXZlMDEpCgpiZXR0ZXJfaXhfYWdfbWUgPC0gd2hpY2goYWJzKGFnX21vZGVybl9tZSkgPCBhYnMoYWdfbW9kZXJuX21lX3N0YW4pKQoKIyBUaGVyZSBhcmUgMTA0IHRoYXQgaGF2ZSBhIHNtYWxsZXIgbWVhbiBlcnJvciB0aGFuIHN0YW5kYXJkCmJldHRlcl9peF9hZzAxX21lIDwtIHdoaWNoKGFicyhhZzAxX21vZGVybl9tZSkgPCBhYnMoYWdfbW9kZXJuX21lX3N0YW4pKQoKIyBUaGVyZSBhcmUgb25seSA0MSB0aGF0IHBhc3MgbGV2ZWwgMiBBTkQgaGF2ZSBzbWFsbGVyIGVycm9yIHRoYW4gc3RhbmRhcmQKbGV2ZWwyX2FuZF9iZXR0ZXJfYWcwMV9peCA8LSBpbnRlcnNlY3QobGV2ZWwyX2l4X3dhdmUwMSwgYmV0dGVyX2l4X2FnMDFfbWUpCgpgYGAKClRoaXMgbmV4dCBwYWlycyBwbG90IGxvb2tzIGF0IGFsbCB0aGUgZW5zZW1ibGUgbWVtYmVycyB0aGF0IGhhdmUgYSBiZXR0ZXIgbWVhbiBhdG1vc3BoZXJpYyBncm93dGggZXJyb3IgdGhhbiBzdGFuZGFyZC4KCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyLCB9ClhfYmV0dGVyX2FnIDwtIHJiaW5kKFhbYmV0dGVyX2l4X2FnX21lLCBdLCBYX3dhdmUwMV90cmFpbltiZXR0ZXJfaXhfYWcwMV9tZSwgXSkKCmJldHRlcl9peCA8LSAxOm5yb3coWF9iZXR0ZXJfYWcpCgpiZXR0ZXJfY29scyA8LSBjKG1ha2VUcmFuc3BhcmVudChyZXAod2F2ZTAwY29sLCBsZW5ndGgoYmV0dGVyX2l4X2FnX21lKSksIDEwMCksIG1ha2VUcmFuc3BhcmVudChyZXAod2F2ZTAxY29sLGxlbmd0aChiZXR0ZXJfaXhfYWcwMV9tZSkpLCAxMDApKQoKcGFpcnMoWF9iZXR0ZXJfYWcsCiAgICAgIGNvbCA9IGJldHRlcl9jb2xzLAogICAgICBnYXAgPSAwLAogICAgICB4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpLAogICAgICBwY2ggPSAxOSwKICAgICAgY2V4PSAwLjgsCiAgICAgIGxvd2VyLnBhbmVsID0gTlVMTCkKYGBgCgpUaGlzIG5leHQgcGxvdCBsb29rcyBhdCBhbGwgdGhlIGVuc2VtYmxlIG1lbWJlcnMgdGhhdCBoYXZlIGEgYmV0dGVyIG1lYW4gYXRtb3NwaGVyaWMgZ3Jvd3RoIGVycm9yIHRoYW4gc3RhbmRhcmQgQU5EIHBhc3MgdGhlIGxldmVsIDIgY29uc3RyYWludHMuICAKClRoZSBudW1iZXIgaXMgc21hbGwgKDQxLzMwMCksIGJ1dCB0aGUgZW5zZW1ibGUgbWVtYmVycyBzZWVtIHNwcmVhZCBhY3Jvc3MgcGFyYW1ldGVyIHNwYWNlLgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTIsIH0KClhfbGV2ZWwyX2FuZF9iZXR0ZXJfYWcwMSA8LSBYW2xldmVsMl9hbmRfYmV0dGVyX2FnMDFfaXgsIF0KcGFpcnMoWF9sZXZlbDJfYW5kX2JldHRlcl9hZzAxLAogICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoJ2JsYWNrJywgMTUwKSwKICAgICAgZ2FwID0gMCwKICAgICAgeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSwKICAgICAgcGNoID0gMTksCiAgICAgIGNleD0gMC44LAogICAgICBsb3dlci5wYW5lbCA9IE5VTEwpCgpgYGAKCgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRSwgfQpsaWJyYXJ5KERpY2VLcmlnaW5nKQoKYWcwMV9vdXRsaWVyX2l4IDwtIHdoaWNoKGFnMDFfbW9kZXJuX21lID4gMTApCgpmaXRfYWdfbWUgPC0ga20ofi4sIGRlc2lnbiA9IFhfd2F2ZTAxX3RyYWluWy1hZzAxX291dGxpZXJfaXgsIF0sIHJlc3BvbnNlID0gYWcwMV9tb2Rlcm5fbWVbLWFnMDFfb3V0bGllcl9peF0sIAogICAgICAgICAgICAgICAgICBjb250cm9sID0gbGlzdChtYXhpdCA9IDIwMCkpCgpgYGAKCmBgYHtyLCB9CgpwbG90KGZpdF9hZ19tZSkKCmBgYAoKYGBge3J9CmxpYnJhcnkoZW10b29scykKbGlicmFyeShpbXB0b29scykKbGlicmFyeSh2aXp0b29scykKCm51bmlmIDwtIDEwMDAwClhfdW5pZiA8LSBzYW1wX3VuaWYobnVuaWYsIG1pbnMgPSByZXAoMCwzMiksIG1heGVzID0gcmVwKDEsIDMyKSkKY29sbmFtZXMoWF91bmlmKSA8LSBjb2xuYW1lcyhYKQpgYGAKCgpgYGB7ciwgfQpwcmVkX3VuaWZfYWcgPC0gcHJlZGljdC5rbShmaXRfYWdfbWUsIG5ld2RhdGEgPSBYX3VuaWYsIHR5cGUgPSAnVUsnICkgCgojIEtlZXBzIGFib3V0IDIwJQpYX2tlcHRfaXggPC0gd2hpY2goYWJzKHByZWRfdW5pZl9hZyRtZWFuKSA8IGFicyhhZ19tb2Rlcm5fbWVfc3RhbikpCgoKKGxlbmd0aChYX2tlcHRfaXgpIC8gbnVuaWYpICogMTAwCiMgdGhpcyBpcyA4JQojWF9rZXB0X2l4IDwtIHdoaWNoKGFicyhwcmVkX3VuaWYkbWVhbikgPCAwLjEpCgoKYGBgCgoKIyMgSW5wdXQgc3BhY2Ugd2l0aCBsb3cgQXRtb3NwaGVyaWMgR3Jvd3RoIEVycm9yClRoaXMgcGFpcnMgcGxvdCBzaG93cyB0aGUgMmQgYW5kIG1hcmdpbmFsIGRlbnNpdHkgb2YgZW11bGF0ZWQgaW5wdXQgcG9pbnRzIHdoZXJlIHRoZSBlbXVsYXRlZCBhdG1vc3BoZXJpYyBncm93dGggaXMgY2xvc2VyIHRvIHRoZSBvYnNlcnZhdGlvbnMgdGhhbiB0aGUgc3RhbmRhcmQgbW9kZWwuCgpUaGlzIHRlY2huaXF1ZSBtaWdodCBwcm92aWRlIGEgdXNlZnVsIHNldCBvZiBwb2ludHMgZm9yIG9wdGltaXNpbmcgdGhlIG1vZGVsIChhdCBsZWFzdCB0byBhdG1vc3BoZXJpYyBncm93dGgpLgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTIsIHdhcm5pbmcgPSBGQUxTRSwgfQojcGFpcnMoWF91bmlmW1hfa2VwdF9peCwgXVsxOjUwLF0sCiMgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoJ2JsYWNrJyw1MCksCiMgICAgICBnYXAgPSAwLAojICAgICAgeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSwKIyAgICAgIHBjaCA9IDE5LAojICAgICAgY2V4PSAwLjgsCiMgICAgICBsb3dlci5wYW5lbCA9IE5VTEwpCgpwYXIob21hID0gYygwLDAsMCwzKSwgYmcgPSAnd2hpdGUnKQoKCnBhbmVsLmhpc3QgPSBmdW5jdGlvbih4LCAuLi4pIHsKICB1c3IgPSBwYXIoInVzciIpOyBvbi5leGl0KHBhcih1c3IpKQogIHBhcih1c3IgPSBjKHVzclsxOjJdLCAwLCAxLjUpKQogIGhpc3QoeCwgZnJlcSA9IEZBTFNFLCBjb2w9ImN5YW4iLCBhZGQ9VFJVRSkgCiAgbGluZXMoZGVuc2l0eSh4KSkKfQoKcGFpcnMoWF91bmlmW1hfa2VwdF9peCwgXSwKICAgICMgIGxhYmVscyA9IDE6ZCwKICAgICAgZ2FwID0gMCwgbG93ZXIucGFuZWwgPSBOVUxMLCB4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpLAogICAgICBwYW5lbCA9IGRmdW5jX3VwLAogICAgICBkaWFnLnBhbmVsID0gcGFuZWwuaGlzdCwKICAgICAgY2V4LmxhYmVscyA9IDEsCiAgICAgIGNvbC5heGlzID0gJ3doaXRlJywKICAgICAgZGZ1bmNfY29sID0gcmIpCgpgYGAKCk5leHQsIGNoZWNrIGVtdWxhdG9ycyBvZiBhbGwgdGhlIG90aGVyIG91dHB1dHMgYW5kIGFwcGx5IHRoZSBjb25zdHJhaW50cyB0byB0aGVtLiBTZWUgaG93IHRoZSBjb25zdHJhaW50cyBjaGFuZ2UuCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KCgojIEZpcnN0LCB0cnkgdGhlIGVtdWxhdG9ycyBidWlsdCB1c2luZyBqdXN0IHdhdmUwMQojZml0X2xpc3RfY29uc3RfbGV2ZWwxYQoKCllfY29uc3RfcHJlZF91bmlmX21lYW4gPC0gbWF0cml4KE5BLCBuY29sID0gbmNvbChZX2NvbnN0X2xldmVsMWFfc2NhbGVkKSwgbnJvdyA9IG5yb3coWF91bmlmKSkKY29sbmFtZXMoWV9jb25zdF9wcmVkX3VuaWZfbWVhbikgPC0gY29sbmFtZXMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkKCllfY29uc3RfcHJlZF91bmlmX3NkIDwtIG1hdHJpeChOQSwgbmNvbCA9IG5jb2woWV9jb25zdF9sZXZlbDFhX3NjYWxlZCksIG5yb3cgPSBucm93KFhfdW5pZikpIApjb2xuYW1lcyhZX2NvbnN0X3ByZWRfdW5pZl9zZCkgPC0gY29sbmFtZXMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkKCmZvcihpIGluIDE6bGVuZ3RoKGZpdF9saXN0X2NvbnN0X2xldmVsMWEpKXsKICAKICBwcmVkX3VuaWYgPC0gcHJlZGljdC5rbShvYmplY3Q9Zml0X2xpc3RfY29uc3RfbGV2ZWwxYVtbaV1dLCBuZXdkYXRhID0gWF91bmlmLCB0eXBlID0gJ1VLJykKICAKICBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWyxpIF0gPC0gcHJlZF91bmlmJG1lYW4KICBZX2NvbnN0X3ByZWRfdW5pZl9zZFssaSBdIDwtIHByZWRfdW5pZiRzZAp9CgoKYGBgCgojIyBJbnB1dCBzcGFjZSB3aXRoIGVtdWxhdGVkIG1lbWJlcnMgcGFzc2luZyBMZXZlbCAyIGNvbnN0cmFpbnRzLgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTIsIHdhcm5pbmcgPSBGQUxTRX0KCmxldmVsMl9peF9lbSA8LSB3aGljaChZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnbmJwX2xuZF9zdW0nXSA+IDAgJgogICAgICAgICAgICAgICAgICAgIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPiAzNSAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCdjU29pbF9sbmRfc3VtJ10gPCAzMDAwICYKICAgICAgICAgICAgICAgICAgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NWZWdfbG5kX3N1bSddID4gMzAwICYgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NWZWdfbG5kX3N1bSddIDwgODAwCiAgICAgICAgICAgICAgICAgICkKCnBhaXJzKFhfdW5pZltsZXZlbDJfaXhfZW0sIF0sCiAgICAjICBsYWJlbHMgPSAxOmQsCiAgICAgIGdhcCA9IDAsIGxvd2VyLnBhbmVsID0gTlVMTCwgeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSwKICAgICAgcGFuZWwgPSBkZnVuY191cCwKICAgICAgZGlhZy5wYW5lbCA9IHBhbmVsLmhpc3QsCiAgICAgIGNleC5sYWJlbHMgPSAxLAogICAgICBjb2wuYXhpcyA9ICd3aGl0ZScsCiAgICAgIGRmdW5jX2NvbCA9IHJiKQoKKGxlbmd0aChsZXZlbDJfaXhfZW0pIC8gbnVuaWYpICogMTAwCmBgYAoKIyMgSW5wdXQgc3BhY2Ugd2l0aCBlbXVsYXRlZCBtZW1iZXJzIHBhc3NpbmcgTGV2ZWwgMiBjb25zdHJhaW50cyBBTkQgbG93IGF0bW9zcGhlcmljIGdyb3d0aCBlcnJvcgpFbXVsYXRlZCBtZW1iZXJzIHBhc3NpbmcgbGV2ZWwyIGNvbnN0cmFpbnRzIEFORCBoYXZpbmcgbG93ZXIgZXJyb3IgaW4gYXRtb3NwaGVyaWMgZ3Jvd3RoIHRoYW4gc3RhbmRhcmQuIAoKUmVkIHBvaW50IGluZGljYXRlcyB0aGUgc3RhbmRhcmQgaW5wdXQuCgpUaGUgcG9zaXRpb24gb2YgdGhlIHN0YW5kYXJkIGlucHV0IHdpdGggcmVnYXJkcyB0byB0aGUgaGlzdG9ncmFtcyBnaXZlIHVzIGFuIGlkZWEgb2Ygd2hhdCB3ZSBtaWdodCBkbyB0byBpbXByb3ZlIHRoZSBzaW11bGF0aW9uIC0gYXQgbGVhc3QgaW4gdGVybXMgb2YgYXRtb3NwaGVyaWMgZ3Jvd3RoLiBJZiB3ZSB3ZXJlIHRvIG1vdmUgdGhlIGlucHV0IHRvd2FyZHMgYXJlYXMgb2YgaGlnaGVyIGRlbnNpdHksIHdlIG1pZ2h0IGV4cGVjdCBhIGJldHRlciBtb2RlbCBwZXJmb3JtYW5jZS4KCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMiwgd2FybmluZyA9IEZBTFNFLCB9CgpYX3N0YW5fbm9ybSA8LSBub3JtYWxpemUobWF0cml4KHJlcCgxLCAzMiksIG5yb3cgPSAxKSwgd3J0ID0gbGhzKQoKbGV2ZWwyX2FuZF9hZ19peF9lbSA8LSB3aGljaChZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnbmJwX2xuZF9zdW0nXSA+IDAgJgogICAgICAgICAgICAgICAgICAgIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPiAzNSAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCdjU29pbF9sbmRfc3VtJ10gPCAzMDAwICYKICAgICAgICAgICAgICAgICAgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NWZWdfbG5kX3N1bSddID4gMzAwICYgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NWZWdfbG5kX3N1bSddIDwgODAwICYKICAgICAgICAgICAgICAgICAgICBhYnMocHJlZF91bmlmX2FnJG1lYW4pIDwgYWJzKGFnX21vZGVybl9tZV9zdGFuKQogICAgICAgICAgICAgICAgICApCgoKCnBhaXJzKHJiaW5kKFhfdW5pZltsZXZlbDJfYW5kX2FnX2l4X2VtLCBdLCBYX3N0YW5fbm9ybSksCiAgICAjICBsYWJlbHMgPSAxOmQsCiAgICAgIGdhcCA9IDAsIGxvd2VyLnBhbmVsID0gTlVMTCwgeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSwKICAgICAgcGFuZWwgPSBkZnVuY191cF90cnV0aCwKICAgICAgZGlhZy5wYW5lbCA9IHBhbmVsLmhpc3QsCiAgICAgIGNleC5sYWJlbHMgPSAxLAogICAgICBjb2wuYXhpcyA9ICd3aGl0ZScsCiAgICAgIGRmdW5jX2NvbCA9IHJiKQoKCihsZW5ndGgobGV2ZWwyX2FuZF9hZ19peF9lbSkgLyBudW5pZikgKiAxMDAKYGBgCiMgRXhwbG9yaW5nIGZ1cnRoZXIgY29uc3RyYWludHMKCkl0J3MgcHJldHR5IGNsZWFyIHRoYXQgdGhlICJpbml0aWFsIiAobGV2ZWwyKSBjb25zdHJhaW50cyBjYW4gYmUgaW1wcm92ZWQgb24uIEhvdyBhYm91dCB0cmVlLCBzaHJ1YiBhbmQgYmFyZSBzb2lsIGZyYWN0aW9ucz8KCkJ1aWxkIGFuIGVuc2VtYmxlIG9mIG1vZGVybiB2ZWdldGF0aW9uIGZyYWN0aW9ucy4KYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KCiMgbnN0YXJ0IDwtIDQ5OQojIG5lbmQgPC0gNzk4CgojaWYgKGZpbGUuZXhpc3RzKCJlbnNlbWJsZV93YXZlMDEucmRhdGEiKSkgewojICBsb2FkKCJlbnNlbWJsZV93YXZlMDEucmRhdGEiKQojfSBlbHNlIHsKeV9uYW1lc19mcmFjX21lYW4gPC0gYygndHJlZUZyYWNfbG5kX21lYW4nLCAnYzNQZnRGcmFjX2xuZF9tZWFuJywgJ2M0UGZ0RnJhY19sbmRfbWVhbicsICdzaHJ1YkZyYWNfbG5kX21lYW4nLCAnYmFyZXNvaWxGcmFjX2xuZF9tZWFuJykKICAKZW5zX3dhdmUwMV9mcmFjX21lYW5fbXYgPC0gbWFrZUp1bGVzRW5zZW1ibGVNb2Rlcm5WYWx1ZShlbnNsb2MgPSBlbnNsb2Nfd2F2ZTAxLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmxpc3QgPSB5X25hbWVzX2ZyYWNfbWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zdGFydCA9IG5zdGFydCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5lbmQgPSBuZW5kLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGl4ID0gMTQ0OjE2NCkgCiAgCiMgIHNhdmUoZW5zX3dhdmUwMV9tdiwgZmlsZSA9ImVuc2VtYmxlX3dhdmUwMS5yZGF0YSIpCiN9CgoKYGBgCgoKYGBge3J9CiMgV2hhdCBpZiB3ZSBhbHNvIGhhZCBhICJ0b2xlcmFuY2UgdG8gZXJyb3IiIHRoYXQgbG9va2VkIGxpa2UgdGhpcy4gQmFzaWNhbGx5IG1ha2luZyB1cCBmb3IgdGhlIGZhY3Qgd2UgaGF2ZW4ndCBnb3QgInN0cm9uZyIgb2JzZXJ2YXRpb25zIG9mIGZyYWN0aW9ucywgYnV0IG1pZ2h0IGhhdmUgYSB0b2xlcmFuY2UgdG8gZXJyb3Igb2Ygc29tZSBvZiB0aGVzZSBwcm9wZXJ0aWVzLiBUaGV5IHdvdWxkIGF0IGxlYXN0IGFsbG93IHVzIHRvIGlkZW50aWZ5IHRyYWRlLW9mZnMuCgojIHRyZWVmcmFjIDIwLSA0MAoKIyBzaHJ1YmZyYWMgPCA1CgojIGJhcmVzb2lsIDwgNDAKCiNkZXNpZ24gPSBYX3dhdmUwMV90cmFpblstYWcwMV9vdXRsaWVyX2l4LCBdLAoKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDl9CiMgbmVlZCB0byAKCgpwYXIobWZyb3cgPSBjKDUsMSksIG1hciA9IGMoMiwyLDIsMSkpCiMgYmluZCBlbnNlbWJsZSAwIChsZXZlbCAxYSkgYW5kIHdhdmUwMSBmcmFjdGlvbnMKWV9mcmFjX2xldmVsMWFfd2F2ZTAxIDwtIHJiaW5kKFlfbGV2ZWwxYVssIHlfbmFtZXNfZnJhY19tZWFuXSwgZW5zX3dhdmUwMV9mcmFjX21lYW5fbXYkZGF0bWF0W3dpdGhvdXRfb3V0bGllcnNfaXhfd2F2ZTAxICwgIF0pCgpmcmFjX2hpc3RfYnJlYWtzIDwtIHNlcShmcm9tID0gMCwgdG8gPSAxMDAsIGJ5ICA9IDUpCgpmb3IoaSBpbiAxOm5jb2woWV9mcmFjX2xldmVsMWFfd2F2ZTAxKSkKewogIAogIGNuYW0gPC0gIGNvbG5hbWVzKFlfZnJhY19sZXZlbDFhX3dhdmUwMSlbaV0KICAKICBoaXN0KFlfZnJhY19sZXZlbDFhX3dhdmUwMVsxOm5yb3coWV9sZXZlbDFhKSxpXSwgbWFpbiA9IGNvbG5hbWVzKFlfZnJhY19sZXZlbDFhX3dhdmUwMSlbaV0sIAogICAgICAgeGxhYiA9ICcnLCB4bGltID0gYygwLDEwMCksIGJyZWFrcyA9IGZyYWNfaGlzdF9icmVha3MsIHlsaW0gPSBjKDAsMzAwKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwxMDApKQogIAogIGhpc3QoWV9mcmFjX2xldmVsMWFfd2F2ZTAxWyhucm93KFlfbGV2ZWwxYSkrMSkgOiBucm93KFlfZnJhY19sZXZlbDFhX3dhdmUwMSksIGldLCAKICAgICAgIGFkZCA9IFRSVUUsIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsMTAwKSwgYnJlYWtzID0gZnJhY19oaXN0X2JyZWFrcyApCiAgCiAgCiAgYWJsaW5lKHYgPSBzdGFuZGFyZF9tb2Rlcm5fdmFsdWVbY25hbV0pCiAgCn0KYGBgCgoKIyMgQnVpbGQgZW11bGF0b3JzIGZvciB0aGUgZnJhY3Rpb24gZGF0YQoKYGBge3J9CiMgQ3JlYXRlIGZpdCBsaXN0cyBmb3IgdGhlIGNvbWJpbmVkIGRhdGEKWV9mcmFjX2xldmVsMWFfd2F2ZTAxX2xpc3QgPC0gbWF0Mmxpc3QoWV9mcmFjX2xldmVsMWFfd2F2ZTAxKQoKZml0X2xpc3RfZnJhY19sZXZlbDFhX3dhdmUwMSA8LSBtY2xhcHBseShYID0gWV9mcmFjX2xldmVsMWFfd2F2ZTAxX2xpc3QsIEZVTiA9IGttLCBmb3JtdWxhID0gfi4sIGRlc2lnbiA9IFhfbGV2ZWwxYV93YXZlMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWMuY29yZXMgPSA0LCBjb250cm9sID0gbGlzdCh0cmFjZSA9IEZBTFNFKSkKCgpgYGAKCkl0IG1pZ2h0IGJlIHVzZWZ1bCB0byB0cnkgZW11bGF0aW5nIGxvZyAoZnJhY3Rpb25zKQoKYGBge3J9Cgpmb3IoaSBpbiAxOmxlbmd0aChmaXRfbGlzdF9mcmFjX2xldmVsMWFfd2F2ZTAxKSl7CiAgCiAgcGxvdChmaXRfbGlzdF9mcmFjX2xldmVsMWFfd2F2ZTAxW1tpXV0pCiAgCiAgfQoKCmBgYAoKIyMgTGVhdmUtb25lLW91dCBhbmFseXNlcyBvZiBlbXVsYXRvciBwcmVkaWN0aW9uIGFjY3VyYWN5CgpgYGB7cn0KCmxvb2xpc3Rfa21fWV9mcmFjX2xldmVsMWFfd2F2ZTAxIDwtIG1jbGFwcGx5KFggPSBmaXRfbGlzdF9mcmFjX2xldmVsMWFfd2F2ZTAxLCBGVU4gPSBsZWF2ZU9uZU91dC5rbSwgdHlwZSA9ICdVSycsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQoKYGBgCgoKCmBgYHtyfQoKCllfZnJhY19wcmVkX3VuaWZfbWVhbiA8LSBtYXRyaXgoTkEsIG5jb2wgPSBuY29sKFlfZnJhY19sZXZlbDFhX3dhdmUwMSksIG5yb3cgPSBucm93KFhfdW5pZikpCmNvbG5hbWVzKFlfZnJhY19wcmVkX3VuaWZfbWVhbikgPC0gY29sbmFtZXMoWV9mcmFjX2xldmVsMWFfd2F2ZTAxKQoKWV9mcmFjX3ByZWRfdW5pZl9zZCA8LSBtYXRyaXgoTkEsIG5jb2wgPSBuY29sKFlfZnJhY19sZXZlbDFhX3dhdmUwMSksIG5yb3cgPSBucm93KFhfdW5pZikpIApjb2xuYW1lcyhZX2ZyYWNfcHJlZF91bmlmX3NkKSA8LSBjb2xuYW1lcyhZX2ZyYWNfbGV2ZWwxYV93YXZlMDEpCgpmb3IoaSBpbiAxOmxlbmd0aChmaXRfbGlzdF9mcmFjX2xldmVsMWFfd2F2ZTAxKSl7CiAgCiAgcHJlZF91bmlmIDwtIHByZWRpY3Qua20ob2JqZWN0ID0gZml0X2xpc3RfZnJhY19sZXZlbDFhX3dhdmUwMVtbaV1dLCBuZXdkYXRhID0gWF91bmlmLCB0eXBlID0gJ1VLJykKICAKICBZX2ZyYWNfcHJlZF91bmlmX21lYW5bLGkgXSA8LSBwcmVkX3VuaWYkbWVhbgogIFlfZnJhY19wcmVkX3VuaWZfc2RbLGkgXSA8LSBwcmVkX3VuaWYkc2QKfQoKcm0ocHJlZF91bmlmKQpnYygpCgpgYGAKCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGVycm9yID0gRkFMU0UgfQojIHRyZWVmcmFjIDIwLSA0MAoKIyBzaHJ1YmZyYWMgPCA1CgojIGJhcmVzb2lsIDwgNDAKCnRyZWVmcmFjX2l4X2VtIDwtIHdoaWNoKFlfZnJhY19wcmVkX3VuaWZfbWVhblssICd0cmVlRnJhY19sbmRfbWVhbiddID4gMjAgJiBZX2ZyYWNfcHJlZF91bmlmX21lYW5bLCAndHJlZUZyYWNfbG5kX21lYW4nXSA8IDQwKQogIAojICBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnbmJwX2xuZF9zdW0nXSA+IDAgJgojICAgICAgICAgICAgICAgICAgICAgIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPiAzNSAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiMgICAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCdjU29pbF9sbmRfc3VtJ10gPCAzMDAwICYKIyAgICAgICAgICAgICAgICAgICAgICBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnY1ZlZ19sbmRfc3VtJ10gPiAzMDAgJiBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnY1ZlZ19sbmRfc3VtJ10gPCA4MDAgJgojICAgICAgICAgICAgICAgICAgICAgICAgJiAmCiAgICAgICAgICAgICAgICAgICAgICAjYWJzKHByZWRfdW5pZl9hZyRtZWFuKSA8IGFicyhhZ19tb2Rlcm5fbWVfc3RhbikKIyAgICAgICAgICAgICAgICAgICkKCgpwYWlycyhyYmluZChYX3VuaWZbdHJlZWZyYWNfaXhfZW0sIF0sIFhfc3Rhbl9ub3JtKSwKICAgICMgIGxhYmVscyA9IDE6ZCwKICAgICAgZ2FwID0gMCwgbG93ZXIucGFuZWwgPSBOVUxMLCB4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpLAogICAgICBwYW5lbCA9IGRmdW5jX3VwX3RydXRoLAogICAgICBkaWFnLnBhbmVsID0gcGFuZWwuaGlzdCwKICAgICAgY2V4LmxhYmVscyA9IDEsCiAgICAgIGNvbC5heGlzID0gJ3doaXRlJywKICAgICAgZGZ1bmNfY29sID0gcmIpCgoKYGBgCgoKYGBge3J9CgoobGVuZ3RoKHRyZWVmcmFjX2l4X2VtKSAvIG51bmlmKSAqIDEwMAoKYGBgCgpgYGB7cn0KCnRyZWVmcmFjX2l4X2VtIDwtIHdoaWNoKFlfZnJhY19wcmVkX3VuaWZfbWVhblssICd0cmVlRnJhY19sbmRfbWVhbiddID4gMjAgJiBZX2ZyYWNfcHJlZF91bmlmX21lYW5bLCAndHJlZUZyYWNfbG5kX21lYW4nXSA8IDQwKQogIAoKYGBgCgojIyBTZXF1ZW50aWFsbHkgYWRkIGNvbnN0cmFpbnRzCgpgYGB7cn0KIyB1c2UgZ2V0IG9yIGFzc2lnbj8KCiMgdXNlIGV2YWwgYW5kIHBhcnNlCmV2YWwocGFyc2UodGV4dD0iNSs1IikpCgoKY3JlYXRlQ29uc3RyYWludFN0cmluZyA8LSBmdW5jdGlvbih5dmVjLCBtaW5zLCBtYXhlcyl7CiAgIyBUaGlzIGZ1bmN0aW9uIGNvbnN0cnVjdHMgYSBsb2dpY2FsIGV4cHJlc3Npb24gYXMgYSBzdHJpbmcgdG8gYmUgZXZhbHVhdGVkCiAgb3V0IDwtICd3aGljaCgnCiAgCiAgZm9yKGkgaW4gMTpsZW5ndGgoeXZlYykpewogICAgCiAgICBpZihpPGxlbmd0aCh5dmVjKSl7CiAgICAgIAogICAgc3ViY29uc3QgPC0gcGFzdGUwKCdZX3VuaWYnLCdbLCcsICciJyx5dmVjW2ldLCciJywnXScsICc+JywgbWluc1tpXSwgJyYnLCAnWV91bmlmJywnWywnLCAnIicseXZlY1tpXSwgJyInLCddJyAsICc8JywgbWF4ZXNbaV0sICcmJykKICAgIH0KICAgIAogICAgZWxzZXsKICAgICBzdWJjb25zdCA8LSAgcGFzdGUwKCdZX3VuaWYnLCdbLCcsICciJyx5dmVjW2ldLCciJywnXScsICc+JywgbWluc1tpXSwgJyYnLCAnWV91bmlmJywnWywnLCAnIicseXZlY1tpXSwgJyInLCddJyAsICc8JywgbWF4ZXNbaV0pCiAgICB9CiAgIG91dCA8LSAgcGFzdGUwKG91dCwgc3ViY29uc3QpCiAgfQogIAogIG91dCA8LSBwYXN0ZTAob3V0LCAnKScpCn0KCnl2ZWMgPC0gY29sbmFtZXMoWV9mcmFjX3ByZWRfdW5pZl9tZWFuKQoKdGVzdCA8LSBjcmVhdGVDb25zdHJhaW50U3RyaW5nKHl2ZWMsIG1pbnMgPSBjKC0yMCwtMjAsLTIwLC0yMCwtMjApLCBtYXhlcyA9IGMoMTAwLDEwMCwxMDAsMTAwLDEwMCkpCgoKI2V2YWwocGFyc2UodGV4dCA9IHBhc3RlMCgpKSkKCmlucHV0Q29uc3RyYWludFNpemUgPC0gZnVuY3Rpb24oWV91bmlmLCB5dmVjLCBtaW5zLCBtYXhlcyl7CiAgIyBDYWxjdWxhdGUgdGhlIGluZGljZXMgb2YgWV91bmlmIHRoYXQgYXJlIHdpdGhpbiB0aGUgYm91bmRzIHNldCBieSBtaW5zIGFuZCBtYXhlcwogIAogIAogIGl4X2tlcHQgPC0gZXZhbChwYXJzZSh0ZXh0ID0gY3JlYXRlQ29uc3RyYWludFN0cmluZyh5dmVjPXl2ZWMsIG1pbnMgPSBtaW5zLCBtYXhlcyA9IG1heGVzKSkpCiAgCiAgcHJvcF9rZXB0IDwtIGxlbmd0aChpeF9rZXB0KSAvIG5yb3coWV91bmlmKQogIAogIHJldHVybihsaXN0KGl4X2tlcHQgPSBpeF9rZXB0LCBwcm9wX2tlcHQgPSBwcm9wX2tlcHQpKQogIAp9CiAgCgojIFB1dCBhbGwgb2YgdGhlIGNvbnN0cmFpbnQgaW5wdXRzIHRvZ2V0aGVyCllfcHJlZCA8LSBjYmluZChZX2NvbnN0X3ByZWRfdW5pZl9tZWFuLCBZX2ZyYWNfcHJlZF91bmlmX21lYW4pCgpZX3ByZWRfbWlucyA8LSBjKDAsIDM1LCA3NTAsIDMwMCwgMjAsIDAsIDAsIDAsIDApCllfcHJlZF9tYXhlcyA8LSBjKDEwMDAwLCA4MCwgMzAwMCwgODAwLCA0MCwgMTAwLDEwMCwgNSwgNDApCgpgYGAKCgoKIyMjIGluZGl2aWR1YWwgY29uc3RyYWludAoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA3fQoKaWNzX2luZGl2aWR1YWxfbGlzdCA8LSB2ZWN0b3IobW9kZSA9ICdsaXN0JywgbGVuZ3RoID0gbmNvbChZX3ByZWQpKQoKZm9yKGkgaW4gMTpuY29sKFlfcHJlZCkpewogIAogIHl2ZWMgPC0gY29sbmFtZXMoWV9wcmVkKVtpXQogIGljcyA8LSBpbnB1dENvbnN0cmFpbnRTaXplKFlfdW5pZiA9IHN1YnNldChZX3ByZWQsIHNlbGVjdD15dmVjICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeXZlYyA9IHl2ZWMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlucyA9IFlfcHJlZF9taW5zW2ldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heGVzID0gWV9wcmVkX21heGVzW2ldCiAgKQogIAogIGljc19pbmRpdmlkdWFsX2xpc3RbW2ldXSA8LSBpY3MKICAKfQoKCnBlcmNfa2VwdF9pbmRpdmlkdWFsIDwtIHNhcHBseShpY3NfaW5kaXZpZHVhbF9saXN0LCBGVU4gPSBmdW5jdGlvbih4KSB4JHByb3Bfa2VwdCkgKiAxMDAKCgojcGRmKGZpbGUgPSAnZmlncy9pbmRpdmlkdWFsX2NvbnN0cmFpbnQucGRmJykKcGFyKG1hciA9IGMoMTQsIDQsIDIsIDEpLCBsYXMgPSAxKQpwbG90KDE6OSwgcGVyY19rZXB0X2luZGl2aWR1YWwsIHlsaW0gPSBjKDAsMTAwKSwgdHlwZSA9ICdiJywgYXhlcyA9IEZBTFNFLCB4bGFiID0gJycsCiAgICAgeWxhYiA9ICdQcm9wb3J0aW9uIG9mIGlucHV0IHNwYWNlIHJldGFpbmVkICglKScpCgpheGlzKDIpCgpheGlzKDEsIGxhYmVscyA9IHBhc3RlMChjb2xuYW1lcyhZX3ByZWQpLCAnICgnLFlfcHJlZF9taW5zLCctJywgWV9wcmVkX21heGVzLCcpJyksIGF0ID0gMTo5LCBsYXMgPSAyKQojZGV2Lm9mZigpCgoKYGBgCgoKCiMjIFNlcXVlbnRpYWwgY29uc3RyYWludAoKRWFjaCBjb25zdHJhaW50IGlzIGFkZGVkIHRvIHRoZSBwcmV2aW91cyBjb25zdHJhaW50CgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDd9CgppY3NfbGlzdCA8LSB2ZWN0b3IobW9kZSA9ICdsaXN0JywgbGVuZ3RoID0gbmNvbChZX3ByZWQpKQoKZm9yKGkgaW4gMTpuY29sKFlfcHJlZCkpewogIAogIHl2ZWMgPC0gY29sbmFtZXMoWV9wcmVkKVsxOmldCiAgaWNzIDwtIGlucHV0Q29uc3RyYWludFNpemUoWV91bmlmID0gc3Vic2V0KFlfcHJlZCwgc2VsZWN0PXl2ZWMgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5dmVjID0geXZlYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5zID0gWV9wcmVkX21pbnNbMTppXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhlcyA9IFlfcHJlZF9tYXhlc1sxOmldCiAgKQogIAogIGljc19saXN0W1tpXV0gPC0gaWNzCiAgCn0KCnBlcmNfa2VwdCA8LSBzYXBwbHkoaWNzX2xpc3QsIEZVTiA9IGZ1bmN0aW9uKHgpIHgkcHJvcF9rZXB0KSAqIDEwMAoKI3BkZihmaWxlID0gJ2ZpZ3Mvc2VxdWVudGlhbF9jb25zdHJhaW50LnBkZicpCnBhcihtYXIgPSBjKDE0LCA0LCAyLCAxKSwgbGFzID0gMSkKcGxvdCgxOjksIHBlcmNfa2VwdCwgeWxpbSA9IGMoMCwxMDApLCB0eXBlID0gJ2InLCBheGVzID0gRkFMU0UsIHhsYWIgPSAnJywKICAgICB5bGFiID0gJ1Byb3BvcnRpb24gb2YgaW5wdXQgc3BhY2UgcmV0YWluZWQgKCUpJykKCmF4aXMoMikKYXhpcygxLCBsYWJlbHMgPSBwYXN0ZTAoY29sbmFtZXMoWV9wcmVkKSwgJyAoJyxZX3ByZWRfbWlucywnLScsIFlfcHJlZF9tYXhlcywnKScpLCBhdCA9IDE6OSwgbGFzID0gMikKI2Rldi5vZmYoKQoKCmBgYAoKIyMjIEhvdyBmYXIgZnJvbSBkYXRhIGlzIHRoZSBtb2RlbD8KCmBgYHtyLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gN30KCmZyYWNfY2NpIDwtIGMoZ2xvYmFsX2ZyYWNfY2NpWzFdICsgZ2xvYmFsX2ZyYWNfY2NpWzJdLCBnbG9iYWxfZnJhY19jY2lbM10sIGdsb2JhbF9mcmFjX2NjaVs0XSwgZ2xvYmFsX2ZyYWNfY2NpWzVdLCBnbG9iYWxfZnJhY19jY2lbOF0gKQpmcmFjX212IDwtIGMoc3RhbmRhcmRfbW9kZXJuX3ZhbHVlWyd0cmVlRnJhY19sbmRfbWVhbiddLCBzdGFuZGFyZF9tb2Rlcm5fdmFsdWVbJ2MzUGZ0RnJhY19sbmRfbWVhbiddLCBzdGFuZGFyZF9tb2Rlcm5fdmFsdWVbJ2M0UGZ0RnJhY19sbmRfbWVhbiddLCBzdGFuZGFyZF9tb2Rlcm5fdmFsdWVbJ3NocnViRnJhY19sbmRfbWVhbiddLCBzdGFuZGFyZF9tb2Rlcm5fdmFsdWVbJ2JhcmVzb2lsRnJhY19sbmRfbWVhbiddICApCgoKZnJhY2xhYiA9IGMoJ1RyZWVzJywgJ0MzIGdyYXNzZXMnLCAnQzQgZ3Jhc3NlcycsICdTaHJ1YnMnLCAnQmFyZSBzb2lsJykKcGFyKGxhcyA9IDEsIG1hciA9IGMoOCwgNCwzLDIpKQpwbG90KDE6NSwgZnJhY19jY2kqMTAwLCB5bGltID0gYygwLDUwKSwgcGNoID0gMTksIGNleCA9IDEuNSwgYXhlcyA9IEZBTFNFLCB4bGFiID0gJycpCmF4aXMoMSwgYXQgPSAxOjUsIGxhYmVscyA9IGZyYWNsYWIsIGxhcyA9IDIpCmF4aXMoMikKcG9pbnRzKDE6NSwgZnJhY19tdiwgY29sID0gJ3JlZCcsIHBjaCA9IDE5LCBjZXggPSAxLjUpCmxlZ2VuZCgndG9wbGVmdCcsIGxlZ2VuZCA9IGMoJ0xDIENDSScsICdKVUxFUy1FUy0xcDAnKSwgY29sID0gYygnYmxhY2snLCAncmVkJyksIHBjaCA9IGMoMTksMTkpKQoKCgpgYGAKCgoKIyMjIEhvdyBzaW1pbGFyIGFyZSBlYWNoIG9mIHRoZSBjb25zdHJhaW50cz8KYGBge3J9CgoKCgpgYGAKCgoK